or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

client-configuration.mdconnection-management.mdindex.mdprotocol-clients.mdrequests-responses.mdservice-integration.md

service-integration.mddocs/

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

```