CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-io-helidon-webclient--helidon-webclient

Helidon WebClient is a comprehensive HTTP client library for Java microservices supporting HTTP/1.1, HTTP/2, and protocol negotiation with virtual thread support.

Pending
Overview
Eval results
Files

service-integration.mddocs/

Service Integration

Client-side interceptors and middleware for adding cross-cutting concerns like authentication, logging, metrics, tracing, and custom request/response processing.

Capabilities

WebClient Service Interface

Functional interface for implementing client-side interceptors that can modify requests and responses.

/**
 * Handle request/response processing in the service chain
 * @param chain service chain for proceeding to next service or HTTP call
 * @param clientRequest current client request
 * @return service response
 */
WebClientServiceResponse handle(Chain chain, WebClientServiceRequest clientRequest);

/**
 * Get service name for identification
 * @return service name
 */
default String name();

/**
 * Get service type for categorization
 * @return service type
 */
default String type();

/**
 * Service chain interface for proceeding through the interceptor chain
 */
interface Chain {
    /**
     * Proceed to next service in chain or execute HTTP request
     * @param clientRequest request to process
     * @return service response
     */
    WebClientServiceResponse proceed(WebClientServiceRequest clientRequest);
}

Usage Examples:

import io.helidon.webclient.spi.WebClientService;

// Simple logging service
public class LoggingService implements WebClientService {
    @Override
    public WebClientServiceResponse handle(Chain chain, WebClientServiceRequest request) {
        System.out.println("Request: " + request.method() + " " + request.uri());
        
        WebClientServiceResponse response = chain.proceed(request);
        
        System.out.println("Response: " + response.status());
        return response;
    }
    
    @Override
    public String name() {
        return "logging";
    }
}

// Authentication service
public class AuthenticationService implements WebClientService {
    private final String token;
    
    public AuthenticationService(String token) {
        this.token = token;
    }
    
    @Override
    public WebClientServiceResponse handle(Chain chain, WebClientServiceRequest request) {
        // Add authentication header
        WebClientServiceRequest authenticatedRequest = request.toBuilder()
            .header("Authorization", "Bearer " + token)
            .build();
            
        return chain.proceed(authenticatedRequest);
    }
    
    @Override
    public String name() {
        return "authentication";
    }
}

Service Registration

Register services with WebClient to automatically apply them to all requests.

/**
 * Add a client service to the service chain
 * @param service client service implementation
 * @return builder instance
 */
WebClientConfig.Builder addService(WebClientService service);

/**
 * Configure all client services
 * @param services list of client services
 * @return builder instance
 */
WebClientConfig.Builder services(List<WebClientService> services);

Usage Examples:

// Register services during client creation
WebClient client = WebClient.builder()
    .addService(new LoggingService())
    .addService(new AuthenticationService(token))
    .addService(new MetricsService())
    .addService(new RetryService())
    .build();

// All requests will go through the service chain
String response = client.get("/api/data").requestEntity(String.class);

Service Request Interface

Request representation in the service chain with modification capabilities.

/**
 * Service request interface providing access to request details
 */
public interface WebClientServiceRequest {
    /**
     * Get HTTP method
     * @return HTTP method
     */
    Method method();
    
    /**
     * Get target URI
     * @return request URI
     */
    ClientUri uri();
    
    /**
     * Get request headers
     * @return headers instance
     */
    ClientRequestHeaders headers();
    
    /**
     * Get request properties
     * @return properties map
     */
    Map<String, String> properties();
    
    /**
     * Create builder for modifying request
     * @return request builder
     */
    Builder toBuilder();
    
    /**
     * Builder for creating modified service requests
     */
    interface Builder {
        /**
         * Set HTTP method
         * @param method HTTP method
         * @return builder instance
         */
        Builder method(Method method);
        
        /**
         * Set target URI
         * @param uri request URI
         * @return builder instance
         */
        Builder uri(ClientUri uri);
        
        /**
         * Add or modify header
         * @param name header name
         * @param values header values
         * @return builder instance
         */
        Builder header(String name, String... values);
        
        /**
         * Add request property
         * @param name property name
         * @param value property value
         * @return builder instance
         */
        Builder property(String name, String value);
        
        /**
         * Build modified request
         * @return service request
         */
        WebClientServiceRequest build();
    }
}

Service Response Interface

Response representation in the service chain with access to response details.

/**
 * Service response interface providing access to response details
 */
public interface WebClientServiceResponse {
    /**
     * Get HTTP status
     * @return response status
     */
    Status status();
    
    /**
     * Get response headers
     * @return response headers
     */
    ClientResponseHeaders headers();
    
    /**
     * Get response entity
     * @return readable entity
     */
    ReadableEntity entity();
    
    /**
     * Get request that produced this response
     * @return original request
     */
    WebClientServiceRequest request();
    
    /**
     * Create builder for modifying response
     * @return response builder
     */
    Builder toBuilder();
    
    /**
     * Builder for creating modified service responses
     */
    interface Builder {
        /**
         * Set HTTP status
         * @param status response status
         * @return builder instance
         */
        Builder status(Status status);
        
        /**
         * Add or modify header
         * @param name header name
         * @param values header values
         * @return builder instance
         */
        Builder header(String name, String... values);
        
        /**
         * Set response entity
         * @param entity response entity
         * @return builder instance
         */
        Builder entity(ReadableEntity entity);
        
        /**
         * Build modified response
         * @return service response
         */
        WebClientServiceResponse build();
    }
}

Advanced Service Examples

Complex service implementations demonstrating common patterns.

Retry Service:

public class RetryService implements WebClientService {
    private final int maxRetries;
    private final Duration delay;
    
    public RetryService(int maxRetries, Duration delay) {
        this.maxRetries = maxRetries;
        this.delay = delay;
    }
    
    @Override
    public WebClientServiceResponse handle(Chain chain, WebClientServiceRequest request) {
        WebClientServiceResponse response = null;
        Exception lastException = null;
        
        for (int attempt = 0; attempt <= maxRetries; attempt++) {
            try {
                response = chain.proceed(request);
                
                // Retry on server errors (5xx)
                if (response.status().code() < 500) {
                    return response;
                }
                
                if (attempt < maxRetries) {
                    Thread.sleep(delay.toMillis());
                }
            } catch (Exception e) {
                lastException = e;
                if (attempt < maxRetries) {
                    try {
                        Thread.sleep(delay.toMillis());
                    } catch (InterruptedException ie) {
                        Thread.currentThread().interrupt();
                        break;
                    }
                }
            }
        }
        
        if (response != null) {
            return response;
        }
        
        throw new RuntimeException("Request failed after " + maxRetries + " retries", lastException);
    }
    
    @Override
    public String name() {
        return "retry";
    }
}

Metrics Service:

public class MetricsService implements WebClientService {
    private final MeterRegistry meterRegistry;
    
    public MetricsService(MeterRegistry meterRegistry) {
        this.meterRegistry = meterRegistry;
    }
    
    @Override
    public WebClientServiceResponse handle(Chain chain, WebClientServiceRequest request) {
        Timer.Sample sample = Timer.start(meterRegistry);
        
        try {
            WebClientServiceResponse response = chain.proceed(request);
            
            // Record metrics
            Timer.builder("http.client.requests")
                .tag("method", request.method().text())
                .tag("uri", request.uri().path())
                .tag("status", String.valueOf(response.status().code()))
                .tag("outcome", response.status().code() < 400 ? "SUCCESS" : "ERROR")
                .register(meterRegistry)
                .record(sample.stop());
                
            return response;
        } catch (Exception e) {
            Timer.builder("http.client.requests")
                .tag("method", request.method().text())
                .tag("uri", request.uri().path())
                .tag("status", "UNKNOWN")
                .tag("outcome", "ERROR")
                .register(meterRegistry)
                .record(sample.stop());
                
            throw e;
        }
    }
    
    @Override
    public String name() {
        return "metrics";
    }
}

Circuit Breaker Service:

public class CircuitBreakerService implements WebClientService {
    private final CircuitBreaker circuitBreaker;
    
    public CircuitBreakerService(CircuitBreaker circuitBreaker) {
        this.circuitBreaker = circuitBreaker;
    }
    
    @Override
    public WebClientServiceResponse handle(Chain chain, WebClientServiceRequest request) {
        return circuitBreaker.executeSupplier(() -> {
            WebClientServiceResponse response = chain.proceed(request);
            
            // Consider 5xx responses as failures
            if (response.status().code() >= 500) {
                throw new RuntimeException("Server error: " + response.status());
            }
            
            return response;
        });
    }
    
    @Override
    public String name() {
        return "circuit-breaker";
    }
}

Request/Response Transformation Service:

public class TransformationService implements WebClientService {
    
    @Override
    public WebClientServiceResponse handle(Chain chain, WebClientServiceRequest request) {
        // Transform request - add custom headers
        WebClientServiceRequest transformedRequest = request.toBuilder()
            .header("X-Request-ID", UUID.randomUUID().toString())
            .header("X-Timestamp", Instant.now().toString())
            .property("transformation.applied", "true")
            .build();
        
        WebClientServiceResponse response = chain.proceed(transformedRequest);
        
        // Transform response - add custom header
        return response.toBuilder()
            .header("X-Processed-By", "TransformationService")
            .build();
    }
    
    @Override
    public String name() {
        return "transformation";
    }
}

Service Provider Interface

Service provider for automatic service discovery and registration.

/**
 * Service provider for WebClient services
 */
public interface WebClientServiceProvider {
    /**
     * Create service instance from configuration
     * @param config configuration
     * @param name service name
     * @return service instance
     */
    WebClientService create(Config config, String name);
}

Usage Examples:

// Implement service provider for automatic discovery
public class LoggingServiceProvider implements WebClientServiceProvider {
    @Override
    public WebClientService create(Config config, String name) {
        return new LoggingService(config.get("level").asString().orElse("INFO"));
    }
}

// Register via META-INF/services/io.helidon.webclient.spi.WebClientServiceProvider

Service Ordering and Dependencies

Services are executed in the order they are registered, forming a chain where each service can:

  1. Modify the request before passing it to the next service
  2. Process the response after receiving it from the next service
  3. Handle exceptions that occur in downstream services
  4. Short-circuit the chain by not calling chain.proceed()

Service Chain Execution Order:

// Services registered in this order
WebClient client = WebClient.builder()
    .addService(new AuthenticationService())  // Executes first
    .addService(new LoggingService())         // Executes second
    .addService(new MetricsService())         // Executes third
    .build();

// Execution flow:
// 1. AuthenticationService.handle() -> adds auth header
// 2. LoggingService.handle() -> logs request
// 3. MetricsService.handle() -> starts timer
// 4. HTTP request executed
// 5. MetricsService.handle() <- records metrics  
// 6. LoggingService.handle() <- logs response
// 7. AuthenticationService.handle() <- processes response

Types

@FunctionalInterface
public interface WebClientService extends NamedService {
    WebClientServiceResponse handle(Chain chain, WebClientServiceRequest clientRequest);
    default String name();
    default String type();
    
    interface Chain {
        WebClientServiceResponse proceed(WebClientServiceRequest clientRequest);
    }
}

public interface WebClientServiceRequest {
    Method method();
    ClientUri uri();
    ClientRequestHeaders headers();
    Map<String, String> properties();
    Builder toBuilder();
    
    interface Builder {
        Builder method(Method method);
        Builder uri(ClientUri uri);
        Builder header(String name, String... values);
        Builder property(String name, String value);
        WebClientServiceRequest build();
    }
}

public interface WebClientServiceResponse {
    Status status();
    ClientResponseHeaders headers();
    ReadableEntity entity();
    WebClientServiceRequest request();
    Builder toBuilder();
    
    interface Builder {
        Builder status(Status status);
        Builder header(String name, String... values);
        Builder entity(ReadableEntity entity);
        WebClientServiceResponse build();
    }
}

public interface WebClientServiceProvider {
    WebClientService create(Config config, String name);
}

Install with Tessl CLI

npx tessl i tessl/maven-io-helidon-webclient--helidon-webclient

docs

client-configuration.md

connection-management.md

index.md

protocol-clients.md

requests-responses.md

service-integration.md

tile.json