0
# Service Integration
1
2
Client-side interceptors and middleware for adding cross-cutting concerns like authentication, logging, metrics, tracing, and custom request/response processing.
3
4
## Capabilities
5
6
### WebClient Service Interface
7
8
Functional interface for implementing client-side interceptors that can modify requests and responses.
9
10
```java { .api }
11
/**
12
* Handle request/response processing in the service chain
13
* @param chain service chain for proceeding to next service or HTTP call
14
* @param clientRequest current client request
15
* @return service response
16
*/
17
WebClientServiceResponse handle(Chain chain, WebClientServiceRequest clientRequest);
18
19
/**
20
* Get service name for identification
21
* @return service name
22
*/
23
default String name();
24
25
/**
26
* Get service type for categorization
27
* @return service type
28
*/
29
default String type();
30
31
/**
32
* Service chain interface for proceeding through the interceptor chain
33
*/
34
interface Chain {
35
/**
36
* Proceed to next service in chain or execute HTTP request
37
* @param clientRequest request to process
38
* @return service response
39
*/
40
WebClientServiceResponse proceed(WebClientServiceRequest clientRequest);
41
}
42
```
43
44
**Usage Examples:**
45
46
```java
47
import io.helidon.webclient.spi.WebClientService;
48
49
// Simple logging service
50
public class LoggingService implements WebClientService {
51
@Override
52
public WebClientServiceResponse handle(Chain chain, WebClientServiceRequest request) {
53
System.out.println("Request: " + request.method() + " " + request.uri());
54
55
WebClientServiceResponse response = chain.proceed(request);
56
57
System.out.println("Response: " + response.status());
58
return response;
59
}
60
61
@Override
62
public String name() {
63
return "logging";
64
}
65
}
66
67
// Authentication service
68
public class AuthenticationService implements WebClientService {
69
private final String token;
70
71
public AuthenticationService(String token) {
72
this.token = token;
73
}
74
75
@Override
76
public WebClientServiceResponse handle(Chain chain, WebClientServiceRequest request) {
77
// Add authentication header
78
WebClientServiceRequest authenticatedRequest = request.toBuilder()
79
.header("Authorization", "Bearer " + token)
80
.build();
81
82
return chain.proceed(authenticatedRequest);
83
}
84
85
@Override
86
public String name() {
87
return "authentication";
88
}
89
}
90
```
91
92
### Service Registration
93
94
Register services with WebClient to automatically apply them to all requests.
95
96
```java { .api }
97
/**
98
* Add a client service to the service chain
99
* @param service client service implementation
100
* @return builder instance
101
*/
102
WebClientConfig.Builder addService(WebClientService service);
103
104
/**
105
* Configure all client services
106
* @param services list of client services
107
* @return builder instance
108
*/
109
WebClientConfig.Builder services(List<WebClientService> services);
110
```
111
112
**Usage Examples:**
113
114
```java
115
// Register services during client creation
116
WebClient client = WebClient.builder()
117
.addService(new LoggingService())
118
.addService(new AuthenticationService(token))
119
.addService(new MetricsService())
120
.addService(new RetryService())
121
.build();
122
123
// All requests will go through the service chain
124
String response = client.get("/api/data").requestEntity(String.class);
125
```
126
127
### Service Request Interface
128
129
Request representation in the service chain with modification capabilities.
130
131
```java { .api }
132
/**
133
* Service request interface providing access to request details
134
*/
135
public interface WebClientServiceRequest {
136
/**
137
* Get HTTP method
138
* @return HTTP method
139
*/
140
Method method();
141
142
/**
143
* Get target URI
144
* @return request URI
145
*/
146
ClientUri uri();
147
148
/**
149
* Get request headers
150
* @return headers instance
151
*/
152
ClientRequestHeaders headers();
153
154
/**
155
* Get request properties
156
* @return properties map
157
*/
158
Map<String, String> properties();
159
160
/**
161
* Create builder for modifying request
162
* @return request builder
163
*/
164
Builder toBuilder();
165
166
/**
167
* Builder for creating modified service requests
168
*/
169
interface Builder {
170
/**
171
* Set HTTP method
172
* @param method HTTP method
173
* @return builder instance
174
*/
175
Builder method(Method method);
176
177
/**
178
* Set target URI
179
* @param uri request URI
180
* @return builder instance
181
*/
182
Builder uri(ClientUri uri);
183
184
/**
185
* Add or modify header
186
* @param name header name
187
* @param values header values
188
* @return builder instance
189
*/
190
Builder header(String name, String... values);
191
192
/**
193
* Add request property
194
* @param name property name
195
* @param value property value
196
* @return builder instance
197
*/
198
Builder property(String name, String value);
199
200
/**
201
* Build modified request
202
* @return service request
203
*/
204
WebClientServiceRequest build();
205
}
206
}
207
```
208
209
### Service Response Interface
210
211
Response representation in the service chain with access to response details.
212
213
```java { .api }
214
/**
215
* Service response interface providing access to response details
216
*/
217
public interface WebClientServiceResponse {
218
/**
219
* Get HTTP status
220
* @return response status
221
*/
222
Status status();
223
224
/**
225
* Get response headers
226
* @return response headers
227
*/
228
ClientResponseHeaders headers();
229
230
/**
231
* Get response entity
232
* @return readable entity
233
*/
234
ReadableEntity entity();
235
236
/**
237
* Get request that produced this response
238
* @return original request
239
*/
240
WebClientServiceRequest request();
241
242
/**
243
* Create builder for modifying response
244
* @return response builder
245
*/
246
Builder toBuilder();
247
248
/**
249
* Builder for creating modified service responses
250
*/
251
interface Builder {
252
/**
253
* Set HTTP status
254
* @param status response status
255
* @return builder instance
256
*/
257
Builder status(Status status);
258
259
/**
260
* Add or modify header
261
* @param name header name
262
* @param values header values
263
* @return builder instance
264
*/
265
Builder header(String name, String... values);
266
267
/**
268
* Set response entity
269
* @param entity response entity
270
* @return builder instance
271
*/
272
Builder entity(ReadableEntity entity);
273
274
/**
275
* Build modified response
276
* @return service response
277
*/
278
WebClientServiceResponse build();
279
}
280
}
281
```
282
283
### Advanced Service Examples
284
285
Complex service implementations demonstrating common patterns.
286
287
**Retry Service:**
288
289
```java
290
public class RetryService implements WebClientService {
291
private final int maxRetries;
292
private final Duration delay;
293
294
public RetryService(int maxRetries, Duration delay) {
295
this.maxRetries = maxRetries;
296
this.delay = delay;
297
}
298
299
@Override
300
public WebClientServiceResponse handle(Chain chain, WebClientServiceRequest request) {
301
WebClientServiceResponse response = null;
302
Exception lastException = null;
303
304
for (int attempt = 0; attempt <= maxRetries; attempt++) {
305
try {
306
response = chain.proceed(request);
307
308
// Retry on server errors (5xx)
309
if (response.status().code() < 500) {
310
return response;
311
}
312
313
if (attempt < maxRetries) {
314
Thread.sleep(delay.toMillis());
315
}
316
} catch (Exception e) {
317
lastException = e;
318
if (attempt < maxRetries) {
319
try {
320
Thread.sleep(delay.toMillis());
321
} catch (InterruptedException ie) {
322
Thread.currentThread().interrupt();
323
break;
324
}
325
}
326
}
327
}
328
329
if (response != null) {
330
return response;
331
}
332
333
throw new RuntimeException("Request failed after " + maxRetries + " retries", lastException);
334
}
335
336
@Override
337
public String name() {
338
return "retry";
339
}
340
}
341
```
342
343
**Metrics Service:**
344
345
```java
346
public class MetricsService implements WebClientService {
347
private final MeterRegistry meterRegistry;
348
349
public MetricsService(MeterRegistry meterRegistry) {
350
this.meterRegistry = meterRegistry;
351
}
352
353
@Override
354
public WebClientServiceResponse handle(Chain chain, WebClientServiceRequest request) {
355
Timer.Sample sample = Timer.start(meterRegistry);
356
357
try {
358
WebClientServiceResponse response = chain.proceed(request);
359
360
// Record metrics
361
Timer.builder("http.client.requests")
362
.tag("method", request.method().text())
363
.tag("uri", request.uri().path())
364
.tag("status", String.valueOf(response.status().code()))
365
.tag("outcome", response.status().code() < 400 ? "SUCCESS" : "ERROR")
366
.register(meterRegistry)
367
.record(sample.stop());
368
369
return response;
370
} catch (Exception e) {
371
Timer.builder("http.client.requests")
372
.tag("method", request.method().text())
373
.tag("uri", request.uri().path())
374
.tag("status", "UNKNOWN")
375
.tag("outcome", "ERROR")
376
.register(meterRegistry)
377
.record(sample.stop());
378
379
throw e;
380
}
381
}
382
383
@Override
384
public String name() {
385
return "metrics";
386
}
387
}
388
```
389
390
**Circuit Breaker Service:**
391
392
```java
393
public class CircuitBreakerService implements WebClientService {
394
private final CircuitBreaker circuitBreaker;
395
396
public CircuitBreakerService(CircuitBreaker circuitBreaker) {
397
this.circuitBreaker = circuitBreaker;
398
}
399
400
@Override
401
public WebClientServiceResponse handle(Chain chain, WebClientServiceRequest request) {
402
return circuitBreaker.executeSupplier(() -> {
403
WebClientServiceResponse response = chain.proceed(request);
404
405
// Consider 5xx responses as failures
406
if (response.status().code() >= 500) {
407
throw new RuntimeException("Server error: " + response.status());
408
}
409
410
return response;
411
});
412
}
413
414
@Override
415
public String name() {
416
return "circuit-breaker";
417
}
418
}
419
```
420
421
**Request/Response Transformation Service:**
422
423
```java
424
public class TransformationService implements WebClientService {
425
426
@Override
427
public WebClientServiceResponse handle(Chain chain, WebClientServiceRequest request) {
428
// Transform request - add custom headers
429
WebClientServiceRequest transformedRequest = request.toBuilder()
430
.header("X-Request-ID", UUID.randomUUID().toString())
431
.header("X-Timestamp", Instant.now().toString())
432
.property("transformation.applied", "true")
433
.build();
434
435
WebClientServiceResponse response = chain.proceed(transformedRequest);
436
437
// Transform response - add custom header
438
return response.toBuilder()
439
.header("X-Processed-By", "TransformationService")
440
.build();
441
}
442
443
@Override
444
public String name() {
445
return "transformation";
446
}
447
}
448
```
449
450
### Service Provider Interface
451
452
Service provider for automatic service discovery and registration.
453
454
```java { .api }
455
/**
456
* Service provider for WebClient services
457
*/
458
public interface WebClientServiceProvider {
459
/**
460
* Create service instance from configuration
461
* @param config configuration
462
* @param name service name
463
* @return service instance
464
*/
465
WebClientService create(Config config, String name);
466
}
467
```
468
469
**Usage Examples:**
470
471
```java
472
// Implement service provider for automatic discovery
473
public class LoggingServiceProvider implements WebClientServiceProvider {
474
@Override
475
public WebClientService create(Config config, String name) {
476
return new LoggingService(config.get("level").asString().orElse("INFO"));
477
}
478
}
479
480
// Register via META-INF/services/io.helidon.webclient.spi.WebClientServiceProvider
481
```
482
483
### Service Ordering and Dependencies
484
485
Services are executed in the order they are registered, forming a chain where each service can:
486
487
1. **Modify the request** before passing it to the next service
488
2. **Process the response** after receiving it from the next service
489
3. **Handle exceptions** that occur in downstream services
490
4. **Short-circuit the chain** by not calling `chain.proceed()`
491
492
**Service Chain Execution Order:**
493
494
```java
495
// Services registered in this order
496
WebClient client = WebClient.builder()
497
.addService(new AuthenticationService()) // Executes first
498
.addService(new LoggingService()) // Executes second
499
.addService(new MetricsService()) // Executes third
500
.build();
501
502
// Execution flow:
503
// 1. AuthenticationService.handle() -> adds auth header
504
// 2. LoggingService.handle() -> logs request
505
// 3. MetricsService.handle() -> starts timer
506
// 4. HTTP request executed
507
// 5. MetricsService.handle() <- records metrics
508
// 6. LoggingService.handle() <- logs response
509
// 7. AuthenticationService.handle() <- processes response
510
```
511
512
## Types
513
514
```java { .api }
515
@FunctionalInterface
516
public interface WebClientService extends NamedService {
517
WebClientServiceResponse handle(Chain chain, WebClientServiceRequest clientRequest);
518
default String name();
519
default String type();
520
521
interface Chain {
522
WebClientServiceResponse proceed(WebClientServiceRequest clientRequest);
523
}
524
}
525
526
public interface WebClientServiceRequest {
527
Method method();
528
ClientUri uri();
529
ClientRequestHeaders headers();
530
Map<String, String> properties();
531
Builder toBuilder();
532
533
interface Builder {
534
Builder method(Method method);
535
Builder uri(ClientUri uri);
536
Builder header(String name, String... values);
537
Builder property(String name, String value);
538
WebClientServiceRequest build();
539
}
540
}
541
542
public interface WebClientServiceResponse {
543
Status status();
544
ClientResponseHeaders headers();
545
ReadableEntity entity();
546
WebClientServiceRequest request();
547
Builder toBuilder();
548
549
interface Builder {
550
Builder status(Status status);
551
Builder header(String name, String... values);
552
Builder entity(ReadableEntity entity);
553
WebClientServiceResponse build();
554
}
555
}
556
557
public interface WebClientServiceProvider {
558
WebClientService create(Config config, String name);
559
}
560
```