Helidon SE WebClient Guide

This guide describes how to create a sample Helidon SE project that can be used to run some basic examples using WebClient.

What you need

For this 15 minute tutorial, you will need the following:

A Helidon SE ApplicationYou can use your own application or use the Helidon SE Quickstart to create a sample application.
Java SE 17 (Open JDK 17)Helidon requires Java 17+.
Maven 3.6.1+Helidon requires Maven 3.6.1+.
Docker 18.09+You need Docker if you want to build and deploy Docker containers.
Kubectl 1.16.5+If you want to deploy to Kubernetes, you need kubectl and a Kubernetes cluster (you can install one on your desktop.
Verify Prerequisites
java -version
mvn --version
docker --version
kubectl version
Copied
Setting JAVA_HOME
# On Mac
export JAVA_HOME=`/usr/libexec/java_home -v 17`

# On Linux
# Use the appropriate path to your JDK
export JAVA_HOME=/usr/lib/jvm/jdk-17
Copied

WebClient Features

Helidon’s WebClient is used to perform HTTP REST requests to target endpoints and handle their responses. Built on top of a reactive approach, you are no longer blocked while waiting for the data.

Note: WebClient is still experimental and not intended for production use. APIs and features are not yet fully tested and are subject to change.

WebClient provides the following features:

  • Reactive: As mentioned, the code execution is not blocked by waiting for server response when a request is performed. To avoid memory overflow, the client has built-in back-pressure support.

  • User-friendly: Every client and request is created by a builder pattern, so it improves readability and code maintenance.

  • Following redirects: The WebClient is able to follow the redirect chain and perform requests on the correct endpoint for you. You no longer have to point your client to the correct/final endpoint.

  • Tracing, metrics and security propagation: When you configure the Helidon WebServer to use tracing, metrics and security, the settings are automatically propagated to the WebClient and used during request/response.

WebClient Usage

Create a sample SE project

Generate the project sources using the Helidon SE Maven archetype. The result is a simple project that can be used for the examples in this guide.

Run the Maven archetype:
mvn -U archetype:generate -DinteractiveMode=false \
    -DarchetypeGroupId=io.helidon.archetypes \
    -DarchetypeArtifactId=helidon-quickstart-se \
    -DarchetypeVersion=3.2.16 \
    -DgroupId=io.helidon.examples \
    -DartifactId=helidon-quickstart-se \
    -Dpackage=io.helidon.examples.quickstart.se
Copied

You should now have a directory called helidon-quickstart-se.

Open this directory
cd helidon-quickstart-se
Copied

The Helidon quickstart is a greeting application supporting several HTTP requests such as GET and PUT. Using it will be time saving and allow us to focus on the WebClient features and how to use it.

Modify pom dependency

In the pom.xml, remove the test scope under the WebClient dependency as it will be used not only in the tests.

Remove test scope:
<dependency>
    <groupId>io.helidon.webclient</groupId>
    <artifactId>helidon-webclient</artifactId>
    <scope>test</scope> <!--  Remove this line -->
</dependency>
Copied

Add ClientExample class

In io.helidon.examples.quickstart.se package, create a new class named ClientExample. This class will use the WebClient to send request to the greeting application.

Create ClientExample class:
package io.helidon.examples.quickstart.se;

public class ClientExample {

    public static void main(String[] args) {

    }
}
Copied

Add the following code to create a WebClient instance. The builder approach allows you to create the WebClient with specific settings and improves the readability and simplicity of the code.

Add WebClient instance to the main method:
import io.helidon.media.jsonp.JsonpSupport;
import io.helidon.webclient.WebClient;

WebClient webClient = WebClient.builder()
                .baseUri("http://localhost:8080")   
                .addMediaSupport(JsonpSupport.create())     
                .build();
Copied
  • The base URI of the outbound requests.
  • Register a support for Jsonp.

By default, the Helidon quickstart application runs on localhost:8080. If you changed the host name or port number make sure to modify the base URI as well. Once built, the WebClient can be used to send a GET request to the greeting application.

Send a GET request to the target endpoint:
webClient.get()     
    .path("/greet")     
    .request(String.class)  
    .peek(System.out::println)  
    .await();   
Copied
  • Create a HTTP GET request.
  • Target endpoint path.
  • Execute the request and return Single with response entity handled as a String.
  • Print the response in the console.
  • Wait for server response because of reactive approach.

The path method joins /greet to the WebClient base URI. The target URI for this request becomes http://localhost:8080/greet where the response should be a greeting message. Received response entity will be automatically handled as a String. If no specific type is put into the method request(), WebClientResponse is returned by default. This WebClientResponse object contains response code, headers and non-handled entity.

Run the application

Build the quickstart:
mvn package
Copied

This command will create helidon-quickstart-se.jar in the target folder.

Run the greeting application first:
java -cp target/helidon-quickstart-se.jar io.helidon.examples.quickstart.se.Main
Copied

Open a new command prompt or terminal and run the ClientExample class you just created.

Run the greeting application first:
java -cp target/helidon-quickstart-se.jar io.helidon.examples.quickstart.se.ClientExample
Copied
Output:
{"message":"Hello World!"}
Copied

When the ClientExample finishes its execution, you can stop the Main class by pressing ctrl+c.

Discover other WebClient functionality

In practice, String is not the most useful return type, since it usually needs some more handling. In this case it could be more interesting to return an object such as JsonObject. In the previous step JSON support was added to the WebClient so that it could be used instead of String.

Replace String by JsonObject:
import javax.json.JsonObject;

webClient.get()
    .path("/greet/David")
    .request(JsonObject.class)  
    .peek(System.out::println)
    .await();
Copied
  • Request a JsonObject as return value.

In the URI, the String following greet is a path parameter which allows the application to greet someone.

Output:
{"message":"Hello David!"}
Copied

This time, a JsonObject is printed out in the console. It is possible to change the greeting itself by using a PUT request to /greet/greeting endpoint from the base URI.

Modify the application greeting:
import javax.json.Json;

JsonObject entity = Json.createObjectBuilder() 
    .add("greeting", "Bonjour")
    .build();
webClient.put()     
    .path("/greet/greeting")
    .submit(entity)     
    .thenCompose(response -> webClient.get()    
        .path("/greet/David")
        .request(JsonObject.class))
    .thenAccept(System.out::println)
    .await();
Copied
  • Create a JsonObject with key greeting and value bonjour.
  • Create a PUT request.
  • Submit the JsonObject created earlier.
  • Once done, make a GET call to verify the modification was processed to the greeting.

According to the quickstart documentation, a JSON object can be sent to the application to change the greeting following this structure: {"greeting" : "value"}. The first three lines of code create the JsonObject with the required content. This time, we use the PUT request and submit methods to push the new greeting. One way to check the greeting modification is to execute GET request again and display obtained response. The thenCompose method will execute a GET request after the PUT request is executed.

Output:
{"message":"Bonjour David!"}
Copied

WebClient Metrics

WebClient, like other Helidon components, supports Metrics. The following example introduces the different metrics that can be used to measure WebClient activity. There are two ways to set up metrics: programmatically on the WebClient instance or manually using the configuration file.

Add metrics dependency

There is a specific dependency to use WebClient metrics in your application.

Add the following dependency to pom.xml:
<dependency>
    <groupId>io.helidon.webclient</groupId>
    <artifactId>helidon-webclient-metrics</artifactId>
</dependency>
Copied

Set up metrics on WebClient instance

It is possible to register metrics on WebClient directly into the code. The following example shows a general method that can be used with any metric.

Example of metric creation:
import io.helidon.common.http.Http;
import io.helidon.metrics.RegistryFactory;
import io.helidon.webclient.metrics.WebClientMetrics;
import io.helidon.webclient.spi.WebClientService;
import org.eclipse.microprofile.metrics.MetricRegistry;
import org.eclipse.microprofile.metrics|.Counter;
                                       |.Meter;
                                       |.Timer;
                                       |.ConcurrentGauge;

public static void main(String[] args) {

    MetricRegistry metricFactory = RegistryFactory.getInstance()
            .getRegistry(MetricRegistry.Type.APPLICATION);

    String metricName = "metric.GET.localhost";             

    Counter counter = metricFactory|.counter(metricName);   
                                   |.meter(metricName)
                                   |.timer(metricName)
                                   |.concurrentGauge(metricName)

    WebClientService clientServiceMetric = WebClientMetrics|.counter()
                                                           |.meter()
                                                           |.timer()
                                                           |.gaugeInProgress()

                    .methods(Http.Method.GET)           // OPTIONAL
                    .success(true)                      // OPTIONAL
                    .errors(true)                       // OPTIONAL
                    .description("Metric Description")  // OPTIONAL
                    .nameFormat("counter.%1$s.%2$s")
                    .build();                           
Copied
  • Choose the metric name.
  • Create a metric from metricFactory.
  • Build a WebClient Service for counting the GET requests.

The metric name can indicate what is measured. In this example, the metric target GET requests on the localhost. In order to pass this information to the webclient, the nameFormat method extracts it from the metric name. Otherwise, the metric name can also have nothing in common with its job. In this case, the methods with OPTIONAL comment are not required to be used. The methods will target the chosen HTTP request type. While success and error will respectively measure if a request is successful or failed. The description will add a metric description.

Add the metric service to the WebClient:
WebClient webClient = WebClient.builder()
                .baseUri("http://localhost:8080")
                .addMediaSupport(JsonpSupport.create())
                .addService(clientServiceMetric)           
                .build();
Copied
  • Register the metric service to the webclient.

Simply use the addService method to add the metric to the WebClient on which the metrics will be measured.

Print the metric count at the end of the main method:
System.out.println(metricName + ": " + counter.getCount());
Copied

To quickly check metrics are set up correctly, print the counter at the end of the main method. In this guide, the WebClient uses GET and PUT requests, so metrics can be applied on.

Set up metrics with configuration files

Using the configuration file can reduce the code complexity and make the metrics simpler to use. There is no need to modify the source code but only the configuration file to measure other values. The application.yaml file is the default configuration file for Helidon. It can be used to set up metrics settings.

Example of metric configuration:
client:
  services:
    config:
      metrics:
        - type: METER
          name-format: "client.meter.overall"
        - type: TIMER
          # meter per method
          name-format: "client.meter.%1$s"
        - methods: ["GET"]
          type: COUNTER
          errors: false
          name-format: "client.counter.%1$s.success"
          description: "Counter of successful GET requests"
        - methods: ["PUT", "POST", "DELETE"]
          type: COUNTER
          success: false
          name-format: "wc.counter.%1$s.error"
          description: "Counter of failed PUT, POST and DELETE requests"
        - methods: ["GET"]
          type: GAUGE_IN_PROGRESS
          name-format: "client.inprogress.%2$s"
          description: "In progress requests to host"
Copied

The metrics are located under client.services.config.metrics. The metric setting can start either by its type or methods. The configuration file uses the same keywords as the programmatic way. type defines the kind of metric.

Add the metric service to the WebClient:
Config config = Config.create();       

WebClient webClient = WebClient.builder()
                .baseUri("http://localhost:8080")
                .config(config.get("client"))       
                .addMediaSupport(JsonpSupport.create())
                .build();
Copied
  • Create a Helidon Config instance from default file application.yaml.
  • Configure the WebClient with the client section from application.yaml.

As demonstrated, using the configuration file reduces the amount of code needed in the source code. For more information about metrics, see the Helidon Metrics Guide.