001 002package io.vrap.rmf.base.client; 003 004import static java.util.Objects.requireNonNull; 005 006import java.net.URI; 007import java.time.Duration; 008import java.util.*; 009import java.util.concurrent.ExecutorService; 010import java.util.concurrent.ForkJoinPool; 011import java.util.concurrent.ScheduledExecutorService; 012import java.util.function.Function; 013import java.util.function.Predicate; 014import java.util.function.Supplier; 015 016import javax.annotation.Nullable; 017 018import io.vrap.rmf.base.client.error.HttpExceptionFactory; 019import io.vrap.rmf.base.client.http.*; 020import io.vrap.rmf.base.client.oauth2.*; 021 022import org.slf4j.event.Level; 023 024import dev.failsafe.spi.Scheduler; 025 026/** 027 <p>The ClientBuilder is used to configure and create an {@link ApiHttpClient}. As the ApiHttpClient uses a {@link HandlerStack stack} 028 of middlewares the Builder comes with methods to configure and attach new middlewares. Also it ensures that some default 029 used middlewares are instantiated at the correct location in the middleware stack.</p> 030 031 <p>The default middlewares and services are added as Supplier to be able to override the ones provided by e.g.: {@link #defaultClient(URI)}</p> 032 */ 033public class ClientBuilder implements Builder<ApiHttpClient> { 034 public static final String COMMERCETOOLS = "commercetools"; 035 static final String userAgent = "commercetools-sdk-java-v2/"; 036 037 private URI apiBaseUrl; 038 private boolean useAuthCircuitBreaker; 039 private int authRetries; 040 private Supplier<ErrorMiddleware> errorMiddleware; 041 private Supplier<OAuthMiddleware> oAuthMiddleware; 042 private Supplier<RetryRequestMiddleware> retryMiddleware; 043 private Supplier<QueueRequestMiddleware> queueMiddleware; 044 private Supplier<Middleware> correlationIdMiddleware; 045 private InternalLoggerMiddleware internalLoggerMiddleware; 046 private UserAgentMiddleware userAgentMiddleware; 047 048 private Supplier<TelemetryMiddleware> telemetryMiddleware; 049 private List<Middleware> middlewares = new ArrayList<>(); 050 private Supplier<HandlerStack> stack; 051 private VrapHttpClient httpClient; 052 private VrapHttpClient oauthHttpClient; 053 private Supplier<ResponseSerializer> serializer; 054 private Supplier<HttpExceptionFactory> httpExceptionFactory; 055 056 private Supplier<ExecutorService> oauthExecutorService = ForkJoinPool::new; 057 /** 058 * <p>Creates a default client builder</p> 059 * @return ClientBuilder instance 060 */ 061 public static ClientBuilder of() { 062 return new ClientBuilder(); 063 } 064 065 public static ClientBuilder of(ExecutorService httpClientExecutorService) { 066 return new ClientBuilder(httpClientExecutorService); 067 } 068 069 /** 070 * <p>Creates a client builder with a specific or preconfigured {@link VrapHttpClient} instance. Uses defaults for 071 * the {@link HandlerStack}</p> 072 * @param httpClient the HTTP client to be used 073 * @return ClientBuilder instance 074 */ 075 public static ClientBuilder of(final VrapHttpClient httpClient) { 076 return new ClientBuilder(httpClient); 077 } 078 079 /** 080 * <p>Creates a client builder with a specific or preconfigured {@link ApiHttpClient} instance. Uses defaults for 081 * the {@link HandlerStack}</p> 082 * @param httpClient the HTTP client to be used 083 * @return ClientBuilder instance 084 */ 085 public static ClientBuilder of(final ApiHttpClient httpClient) { 086 return new ClientBuilder(httpClient); 087 } 088 089 /** 090 * <p>Creates a client builder with a specifig {@link HandlerStack}</p> 091 * @param stack the HandlerStack to be used 092 * @return ClientBuilder instance 093 */ 094 public static ClientBuilder of(final HandlerStack stack) { 095 return new ClientBuilder(stack); 096 } 097 098 private ClientBuilder(final HandlerStack stack) { 099 this.stack = () -> stack; 100 ResponseSerializer serializer = ResponseSerializer.of(); 101 this.serializer = () -> serializer; 102 this.httpExceptionFactory = () -> HttpExceptionFactory.of(this.serializer.get()); 103 this.useAuthCircuitBreaker = false; 104 this.authRetries = 1; 105 } 106 107 private ClientBuilder() { 108 this.httpClient = HttpClientSupplier.of(new ForkJoinPool()).get(); 109 this.oauthHttpClient = httpClient; 110 this.stack = stackSupplier(); 111 ResponseSerializer serializer = ResponseSerializer.of(); 112 this.serializer = () -> serializer; 113 this.httpExceptionFactory = () -> HttpExceptionFactory.of(this.serializer.get()); 114 this.useAuthCircuitBreaker = false; 115 this.authRetries = 1; 116 } 117 118 private ClientBuilder(ExecutorService httpClientExecutorService) { 119 this.httpClient = HttpClientSupplier.of(httpClientExecutorService).get(); 120 this.oauthHttpClient = httpClient; 121 this.stack = stackSupplier(); 122 ResponseSerializer serializer = ResponseSerializer.of(); 123 this.serializer = () -> serializer; 124 this.httpExceptionFactory = () -> HttpExceptionFactory.of(this.serializer.get()); 125 this.useAuthCircuitBreaker = false; 126 this.authRetries = 1; 127 } 128 129 private ClientBuilder(final ApiHttpClient httpClient) { 130 this.httpClient = httpClient; 131 this.oauthHttpClient = HttpClientSupplier.of(new ForkJoinPool()).get(); 132 this.stack = stackSupplier(); 133 ResponseSerializer serializer = ResponseSerializer.of(); 134 this.serializer = () -> serializer; 135 this.httpExceptionFactory = () -> HttpExceptionFactory.of(this.serializer.get()); 136 this.authRetries = 1; 137 } 138 139 private ClientBuilder(final VrapHttpClient httpClient) { 140 this.httpClient = httpClient; 141 this.oauthHttpClient = httpClient; 142 this.stack = stackSupplier(); 143 ResponseSerializer serializer = ResponseSerializer.of(); 144 this.serializer = () -> serializer; 145 this.httpExceptionFactory = () -> HttpExceptionFactory.of(this.serializer.get()); 146 this.authRetries = 1; 147 } 148 149 /** 150 * Ensures the order of default middlewares to create a {@link HandlerStack} 151 * @return HandlerStack supplier method 152 */ 153 private Supplier<HandlerStack> stackSupplier() { 154 return () -> { 155 final List<Middleware> middlewareStack = new ArrayList<>(); 156 Optional.ofNullable(telemetryMiddleware).map(m -> middlewareStack.add(m.get())); 157 Optional.ofNullable(errorMiddleware).map(m -> middlewareStack.add(m.get())); 158 Optional.ofNullable(internalLoggerMiddleware).map(middlewareStack::add); 159 Optional.ofNullable(userAgentMiddleware).map(middlewareStack::add); 160 Optional.ofNullable(oAuthMiddleware).map(m -> middlewareStack.add(m.get())); 161 Optional.ofNullable(retryMiddleware).map(m -> middlewareStack.add(m.get())); 162 Optional.ofNullable(queueMiddleware).map(m -> middlewareStack.add(m.get())); 163 Optional.ofNullable(correlationIdMiddleware).map(m -> middlewareStack.add(m.get())); 164 middlewareStack.addAll(middlewares); 165 return HandlerStack.create(HttpHandler.create(requireNonNull(httpClient)), middlewareStack); 166 }; 167 } 168 169 /** 170 * Ensures the order of default middlewares to create a {@link HandlerStack} 171 * @return HandlerStack supplier method 172 */ 173 private Supplier<HandlerStack> oauthHandlerSupplier() { 174 return () -> { 175 final List<Middleware> middlewareStack = new ArrayList<>(); 176 Optional.ofNullable(userAgentMiddleware).map(middlewareStack::add); 177 Optional.ofNullable(correlationIdMiddleware).map(m -> middlewareStack.add(m.get())); 178 return HandlerStack.create(HttpHandler.create(requireNonNull(oauthHttpClient)), middlewareStack); 179 }; 180 } 181 182 /** 183 * deactivates the circuit breaker for authentication 184 * @return ClientBuilder instance 185 */ 186 public ClientBuilder withoutAuthCircuitBreaker() { 187 this.useAuthCircuitBreaker = false; 188 return this; 189 } 190 191 /** 192 * activates the circuit breaker for authentication. Upon erroneous authentication e.g. the authentication 193 * middleware will open the circuit breaker and retry regularly. 194 * @return ClientBuilder instance 195 */ 196 public ClientBuilder withAuthCircuitBreaker() { 197 this.useAuthCircuitBreaker = true; 198 return this; 199 } 200 201 /** 202 * @param authRetries number of retries for authentication before giving up. 203 * @return ClientBuilder instance 204 */ 205 public ClientBuilder withAuthRetries(final int authRetries) { 206 this.authRetries = authRetries; 207 return this; 208 } 209 210 /** 211 * @param stack {@link HandlerStack} to be used for the HTTP requests 212 * @return ClientBuilder instance 213 */ 214 public ClientBuilder withHandlerStack(final HandlerStack stack) { 215 this.stack = () -> stack; 216 return this; 217 } 218 219 /** 220 * @param httpClient {@link VrapHttpClient} to be used for the HTTP requests 221 * @return ClientBuilder instance 222 */ 223 public ClientBuilder withHttpClient(final VrapHttpClient httpClient) { 224 this.httpClient = httpClient; 225 return this; 226 } 227 228 /** 229 * @param httpClient {@link VrapHttpClient} to be used for the OAuth requests 230 * @return ClientBuilder instance 231 */ 232 public ClientBuilder withOAuthHttpClient(final VrapHttpClient httpClient) { 233 this.oauthHttpClient = httpClient; 234 return this; 235 } 236 237 /** 238 * @param serializer {@link ResponseSerializer} to be used for de-/serialization 239 * @return ClientBuilder instance 240 */ 241 public ClientBuilder withSerializer(final ResponseSerializer serializer) { 242 this.serializer = () -> serializer; 243 return this; 244 } 245 246 /** 247 * @param serializer {@link ResponseSerializer} to be used for de-/serialization 248 * @return ClientBuilder instance 249 */ 250 public ClientBuilder withSerializer(final Supplier<ResponseSerializer> serializer) { 251 this.serializer = serializer; 252 return this; 253 } 254 255 /** 256 * @param factory {@link HttpExceptionFactory} to be used for creating Exceptions based on response status code 257 * @return ClientBuilder instance 258 */ 259 public ClientBuilder withHttpExceptionFactory(final HttpExceptionFactory factory) { 260 this.httpExceptionFactory = () -> factory; 261 return this; 262 } 263 264 /** 265 * @param factory function to create {@link HttpExceptionFactory} to be used for creating Exceptions based on response 266 * status code with the configured {@link ResponseSerializer} 267 * @return ClientBuilder instance 268 */ 269 public ClientBuilder withHttpExceptionFactory(final Function<ResponseSerializer, HttpExceptionFactory> factory) { 270 this.httpExceptionFactory = () -> factory.apply(serializer.get()); 271 return this; 272 } 273 274 /** 275 * configures the Factory for HTTP exception in case. 276 * @param factory {@link HttpExceptionFactory} to be used for creating Exceptions based on response status code 277 * @return ClientBuilder instance 278 */ 279 public ClientBuilder withHttpExceptionFactory(final Supplier<HttpExceptionFactory> factory) { 280 this.httpExceptionFactory = factory; 281 return this; 282 } 283 284 /** 285 * configures an ExecutorService to be used for the Middlewares 286 * @param executorService supplier of the executor service to be used 287 * @return ClientBuilder instance 288 */ 289 public ClientBuilder withOAuthExecutorService(final Supplier<ExecutorService> executorService) { 290 this.oauthExecutorService = executorService; 291 return this; 292 } 293 294 /** 295 * configures an ExecutorService to be used for the Middlewares 296 * @param executorService executor service to be used 297 * @return ClientBuilder instance 298 */ 299 public ClientBuilder withOAuthExecutorService(final ExecutorService executorService) { 300 this.oauthExecutorService = () -> executorService; 301 return this; 302 } 303 304 /** 305 * <p>Configures a client with the default middlewares and the given baseUrl</p> 306 * <p>The following middlewares and services are configured: 307 * <ul> 308 * <li>{@link ErrorMiddleware}</li> 309 * <li>{@link ResponseSerializer}</li> 310 * <li>{@link InternalLoggerFactory}</li> 311 * <li>{@link UserAgentMiddleware}</li> 312 * <li>{@link AcceptGZipMiddleware}</li> 313 * </ul> 314 * </p> 315 * @param apiBaseUrl base URI for the API 316 * @return ClientBuilder instance 317 */ 318 public ClientBuilder defaultClient(final URI apiBaseUrl) { 319 return withApiBaseUrl(apiBaseUrl).withErrorMiddleware() 320 .withSerializer(ResponseSerializer.of()) 321 .withInternalLoggerFactory((request, topic) -> InternalLogger.getLogger(COMMERCETOOLS + "." + topic)) 322 .withUserAgentSupplier(ClientBuilder::buildDefaultUserAgent) 323 .addAcceptGZipMiddleware(); 324 } 325 326 /** 327 * <p>Configures a client with the default middlewares and the given baseUrl</p> 328 * <p>The following middlewares and services are configured: 329 * <ul> 330 * <li>{@link ErrorMiddleware}</li> 331 * <li>{@link ResponseSerializer}</li> 332 * <li>{@link InternalLoggerFactory}</li> 333 * <li>{@link UserAgentMiddleware}</li> 334 * <li>{@link AcceptGZipMiddleware}</li> 335 * </ul> 336 * </p> 337 * @param apiBaseUrl base URI for the API 338 * @return ClientBuilder instance 339 */ 340 public ClientBuilder defaultClient(final String apiBaseUrl) { 341 return defaultClient(URI.create(apiBaseUrl)); 342 } 343 344 /** 345 * <p>Configures a client with the default middlewares and the given baseUrl</p> 346 * <p>The following middlewares and services are configured: 347 * <ul> 348 * <li>{@link ErrorMiddleware}</li> 349 * <li>{@link ResponseSerializer}</li> 350 * <li>{@link InternalLoggerFactory}</li> 351 * <li>{@link UserAgentMiddleware}</li> 352 * <li>{@link AcceptGZipMiddleware}</li> 353 * </ul> 354 * </p> 355 * @param apiBaseUrl base URI for the API 356 * @param credentials {@link ClientCredentials} to be used 357 * @param tokenEndpoint token endpoint URI to be used for authentication 358 * @return ClientBuilder instance 359 */ 360 public ClientBuilder defaultClient(final String apiBaseUrl, final ClientCredentials credentials, 361 final String tokenEndpoint) { 362 return defaultClient(apiBaseUrl).withClientCredentialsFlow(credentials, tokenEndpoint); 363 } 364 365 /** 366 * <p>Configures a client with the default middlewares and the given baseUrl</p> 367 * <p>The following middlewares and services are configured: 368 * <ul> 369 * <li>{@link ErrorMiddleware}</li> 370 * <li>{@link ResponseSerializer}</li> 371 * <li>{@link InternalLoggerFactory}</li> 372 * <li>{@link UserAgentMiddleware}</li> 373 * <li>{@link AcceptGZipMiddleware}</li> 374 * </ul> 375 * </p> 376 * @param credentials {@link ClientCredentials} to be used 377 * @param serviceRegionConfig {@link ServiceRegionConfig} to be used 378 * @return ClientBuilder instance 379 */ 380 public ClientBuilder defaultClient(final ClientCredentials credentials, 381 final ServiceRegionConfig serviceRegionConfig) { 382 return defaultClient(serviceRegionConfig.getApiUrl()).withClientCredentialsFlow(credentials, 383 serviceRegionConfig.getOAuthTokenUrl()); 384 } 385 386 /** 387 * @deprecated use {@link #withClientCredentialsFlow(ClientCredentials, String)} instead 388 * @param credentials OAuth credentials 389 * @param tokenEndpoint OAuth endpoint 390 * @return client builder 391 */ 392 @Deprecated 393 public ClientBuilder withClientCredentials(final ClientCredentials credentials, final String tokenEndpoint) { 394 return withClientCredentialsFlow(credentials, tokenEndpoint); 395 } 396 397 /** 398 * @deprecated use {@link #withClientCredentialsFlow(ClientCredentials, String, VrapHttpClient)} instead 399 * @param credentials OAuth credentials 400 * @param tokenEndpoint OAuth endpoint 401 * @param httpClient HTTP client to be used 402 * @return client builder 403 */ 404 @Deprecated 405 public ClientBuilder withClientCredentials(final ClientCredentials credentials, final String tokenEndpoint, 406 VrapHttpClient httpClient) { 407 return withClientCredentialsFlow(credentials, tokenEndpoint, httpClient); 408 } 409 410 /** 411 * configure the client to use client credentials flow 412 * @param credentials {@link ClientCredentials} to be used for authentication 413 * @param tokenEndpoint URI to be used for authentication 414 * @return ClientBuilder instance 415 */ 416 public ClientBuilder withClientCredentialsFlow(final ClientCredentials credentials, final URI tokenEndpoint) { 417 return withClientCredentialsFlow(credentials, tokenEndpoint.toString()); 418 } 419 420 /** 421 * configure the client to use client credentials flow 422 * @param credentials {@link ClientCredentials} to be used for authentication 423 * @param tokenEndpoint URI to be used for authentication 424 * @param httpClientSupplier {@link HandlerStack} to use for authentication 425 * @return ClientBuilder instance 426 */ 427 public ClientBuilder withClientCredentialsFlow(final ClientCredentials credentials, final URI tokenEndpoint, 428 Supplier<HandlerStack> httpClientSupplier) { 429 return withClientCredentialsFlow(credentials, tokenEndpoint.toString(), httpClientSupplier); 430 } 431 432 /** 433 * configure the client to use client credentials flow 434 * @param credentials {@link ClientCredentials} to be used for authentication 435 * @param tokenEndpoint URI to be used for authentication 436 * @param httpClient {@link VrapHttpClient} to use for authentication 437 * @return ClientBuilder instance 438 */ 439 public ClientBuilder withClientCredentialsFlow(final ClientCredentials credentials, final URI tokenEndpoint, 440 VrapHttpClient httpClient) { 441 return withClientCredentialsFlow(credentials, tokenEndpoint.toString(), httpClient); 442 } 443 444 private TokenSupplier createClientCredentialsTokenSupplier(final ClientCredentials credentials, 445 final String tokenEndpoint, final VrapHttpClient httpClient) { 446 return new ClientCredentialsTokenSupplier(credentials.getClientId(), credentials.getClientSecret(), 447 credentials.getScopes(), tokenEndpoint, httpClient); 448 } 449 450 /** 451 * configure the client to use client credentials flow 452 * @param credentials {@link ClientCredentials} to be used for authentication 453 * @param tokenEndpoint URI to be used for authentication 454 * @return ClientBuilder instance 455 */ 456 public ClientBuilder withClientCredentialsFlow(final ClientCredentials credentials, final String tokenEndpoint) { 457 return withClientCredentialsFlow(credentials, tokenEndpoint, oauthHandlerSupplier()); 458 } 459 460 /** 461 * configure the client to use client credentials flow 462 * @param credentials {@link ClientCredentials} to be used for authentication 463 * @param tokenEndpoint URI to be used for authentication 464 * @param httpClientSupplier {@link VrapHttpClient} to use for authentication 465 * @return ClientBuilder instance 466 */ 467 public ClientBuilder withClientCredentialsFlow(final ClientCredentials credentials, final String tokenEndpoint, 468 Supplier<HandlerStack> httpClientSupplier) { 469 return withTokenSupplier(() -> createInMemoryTokenSupplier( 470 createClientCredentialsTokenSupplier(credentials, tokenEndpoint, httpClientSupplier.get()))); 471 } 472 473 /** 474 * configure the client to use client credentials flow 475 * @param credentials {@link ClientCredentials} to be used for authentication 476 * @param tokenEndpoint URI to be used for authentication 477 * @param httpClient {@link VrapHttpClient} to use for authentication 478 * @return ClientBuilder instance 479 */ 480 public ClientBuilder withClientCredentialsFlow(final ClientCredentials credentials, final String tokenEndpoint, 481 VrapHttpClient httpClient) { 482 return withTokenSupplier(() -> createInMemoryTokenSupplier( 483 createClientCredentialsTokenSupplier(credentials, tokenEndpoint, httpClient))); 484 } 485 486 /** 487 * configure the client to use client credentials flow 488 * @param token {@link AuthenticationToken} to be used requests 489 * @return ClientBuilder instance 490 */ 491 public ClientBuilder withStaticTokenFlow(final AuthenticationToken token) { 492 return withTokenSupplier(new StaticTokenSupplier(token)); 493 } 494 495 /** 496 * configure the client to use anonymous session flow 497 * @param credentials {@link ClientCredentials} to be used for authentication 498 * @param tokenEndpoint URI to be used for authentication 499 * @return ClientBuilder instance 500 */ 501 public ClientBuilder withAnonymousSessionFlow(final ClientCredentials credentials, final String tokenEndpoint) { 502 return withAnonymousSessionFlow(credentials, tokenEndpoint, oauthHandlerSupplier()); 503 } 504 505 /** 506 * configure the client to use anonymous session flow 507 * @param credentials {@link ClientCredentials} to be used for authentication 508 * @param tokenEndpoint URI to be used for authentication 509 * @param httpClientSupplier {@link HandlerStack} to use for authentication 510 * @return ClientBuilder instance 511 */ 512 public ClientBuilder withAnonymousSessionFlow(final ClientCredentials credentials, final String tokenEndpoint, 513 Supplier<HandlerStack> httpClientSupplier) { 514 return withTokenSupplier(() -> createInMemoryTokenSupplier( 515 createAnonymousSessionTokenSupplier(credentials, tokenEndpoint, httpClientSupplier.get()))); 516 } 517 518 /** 519 * configure the client to use anonymous session flow 520 * @param credentials {@link ClientCredentials} to be used for authentication 521 * @param tokenEndpoint URI to be used for authentication 522 * @param httpClient {@link VrapHttpClient} to use for authentication 523 * @return ClientBuilder instance 524 */ 525 public ClientBuilder withAnonymousSessionFlow(final ClientCredentials credentials, final String tokenEndpoint, 526 VrapHttpClient httpClient) { 527 return withTokenSupplier(() -> createInMemoryTokenSupplier( 528 createAnonymousSessionTokenSupplier(credentials, tokenEndpoint, httpClient))); 529 } 530 531 private TokenSupplier createAnonymousSessionTokenSupplier(final ClientCredentials credentials, 532 final String tokenEndpoint, final VrapHttpClient httpClient) { 533 return new AnonymousSessionTokenSupplier(credentials.getClientId(), credentials.getClientSecret(), 534 credentials.getScopes(), tokenEndpoint, httpClient); 535 } 536 537 /** 538 * configure the client to use anonymous & refresh token flow 539 * @param credentials {@link ClientCredentials} to be used for authentication 540 * @param anonTokenEndpoint URI to be used for anonymous token authentication 541 * @param refreshTokenEndpoint URI to be used for refresh token authentication 542 * @param storage {@link TokenStorage} for the authentication tokens 543 * @return ClientBuilder instance 544 */ 545 public ClientBuilder withAnonymousRefreshFlow(final ClientCredentials credentials, final String anonTokenEndpoint, 546 final String refreshTokenEndpoint, final TokenStorage storage) { 547 return withAnonymousRefreshFlow(credentials, anonTokenEndpoint, refreshTokenEndpoint, storage, 548 oauthHandlerSupplier()); 549 } 550 551 /** 552 * configure the client to use anonymous & refresh token flow 553 * @param credentials {@link ClientCredentials} to be used for authentication 554 * @param anonTokenEndpoint URI to be used for anonymous token authentication 555 * @param refreshTokenEndpoint URI to be used for refresh token authentication 556 * @param storage {@link TokenStorage} for the authentication tokens 557 * @param httpClientSupplier {@link HandlerStack} to be used for authentication 558 * @return ClientBuilder instance 559 */ 560 public ClientBuilder withAnonymousRefreshFlow(final ClientCredentials credentials, final String anonTokenEndpoint, 561 final String refreshTokenEndpoint, final TokenStorage storage, Supplier<HandlerStack> httpClientSupplier) { 562 return withTokenSupplier(() -> createAnonymousRefreshFlowSupplier(credentials, anonTokenEndpoint, 563 refreshTokenEndpoint, storage, httpClientSupplier.get())); 564 } 565 566 /** 567 * configure the client to use anonymous & refresh token flow 568 * @param credentials {@link ClientCredentials} to be used for authentication 569 * @param anonTokenEndpoint URI to be used for anonymous token authentication 570 * @param refreshTokenEndpoint URI to be used for refresh token authentication 571 * @param storage {@link TokenStorage} for the authentication tokens 572 * @param httpClient {@link VrapHttpClient} to be used for authentication 573 * @return ClientBuilder instance 574 */ 575 public ClientBuilder withAnonymousRefreshFlow(final ClientCredentials credentials, final String anonTokenEndpoint, 576 final String refreshTokenEndpoint, final TokenStorage storage, VrapHttpClient httpClient) { 577 return withTokenSupplier(() -> createAnonymousRefreshFlowSupplier(credentials, anonTokenEndpoint, 578 refreshTokenEndpoint, storage, httpClient)); 579 } 580 581 private TokenSupplier createInMemoryTokenSupplier(TokenSupplier tokenSupplier) { 582 return Optional.ofNullable(oauthExecutorService) 583 .map(executorService -> new InMemoryTokenSupplier(executorService.get(), tokenSupplier)) 584 .orElse(new InMemoryTokenSupplier(tokenSupplier)); 585 } 586 587 private TokenSupplier createAnonymousRefreshFlowSupplier(final ClientCredentials credentials, 588 final String anonTokenEndpoint, final String refreshTokenEndpoint, final TokenStorage tokenStorage, 589 final VrapHttpClient httpClient) { 590 final RefreshFlowTokenSupplier refreshFlowTokenSupplier = createRefreshFlowSupplier(credentials, 591 refreshTokenEndpoint, tokenStorage, httpClient); 592 593 final AnonymousFlowTokenSupplier anonymousFlowTokenSupplier = new AnonymousFlowTokenSupplier( 594 credentials.getClientId(), credentials.getClientSecret(), credentials.getScopes(), anonTokenEndpoint, 595 refreshFlowTokenSupplier, httpClient); 596 597 return new TokenStorageSupplier(tokenStorage, anonymousFlowTokenSupplier); 598 } 599 600 private RefreshFlowTokenSupplier createRefreshFlowSupplier(final ClientCredentials credentials, 601 final String tokenEndpoint, final TokenStorage tokenStorage, final VrapHttpClient httpClient) { 602 return new RefreshFlowTokenSupplier(credentials.getClientId(), credentials.getClientSecret(), tokenEndpoint, 603 tokenStorage, httpClient); 604 } 605 606 /** 607 * configure the client to use password flow 608 * @param credentials {@link ClientCredentials} to be used for authentication 609 * @param tokenEndpoint URI to be used for password flow authentication 610 * @param email customer email 611 * @param password customer password 612 * @return ClientBuilder instance 613 */ 614 public ClientBuilder withGlobalCustomerPasswordFlow(final ClientCredentials credentials, final String email, 615 final String password, final String tokenEndpoint) { 616 return withGlobalCustomerPasswordFlow(credentials, email, password, tokenEndpoint, oauthHandlerSupplier()); 617 } 618 619 /** 620 * configure the client to use password flow 621 * @param credentials {@link ClientCredentials} to be used for authentication 622 * @param tokenEndpoint URI to be used for password flow authentication 623 * @param email customer email 624 * @param password customer password 625 * @param httpClientSupplier {@link HandlerStack} to use for authentication 626 * @return ClientBuilder instance 627 */ 628 public ClientBuilder withGlobalCustomerPasswordFlow(final ClientCredentials credentials, final String email, 629 final String password, final String tokenEndpoint, Supplier<HandlerStack> httpClientSupplier) { 630 return withTokenSupplier( 631 () -> createInMemoryTokenSupplier(createGlobalCustomerPasswordTokenSupplier(credentials, email, password, 632 tokenEndpoint, httpClientSupplier.get()))); 633 } 634 635 /** 636 * configure the client to use password flow 637 * @param credentials {@link ClientCredentials} to be used for authentication 638 * @param tokenEndpoint URI to be used for password flow authentication 639 * @param email customer email 640 * @param password customer password 641 * @param httpClient {@link VrapHttpClient} to use for authentication 642 * @return ClientBuilder instance 643 */ 644 public ClientBuilder withGlobalCustomerPasswordFlow(final ClientCredentials credentials, final String email, 645 final String password, final String tokenEndpoint, VrapHttpClient httpClient) { 646 return withTokenSupplier(() -> createInMemoryTokenSupplier( 647 createGlobalCustomerPasswordTokenSupplier(credentials, email, password, tokenEndpoint, httpClient))); 648 } 649 650 private TokenSupplier createGlobalCustomerPasswordTokenSupplier(final ClientCredentials credentials, 651 final String email, final String password, final String tokenEndpoint, final VrapHttpClient httpClient) { 652 return new GlobalCustomerPasswordTokenSupplier(credentials.getClientId(), credentials.getClientSecret(), email, 653 password, credentials.getScopes(), tokenEndpoint, httpClient); 654 } 655 656 /** 657 * add middleware to inject an <i>Accept: gzip</i> header 658 * @return ClientBuilder instance 659 */ 660 public ClientBuilder addAcceptGZipMiddleware() { 661 return addMiddleware(AcceptGZipMiddleware.of()); 662 } 663 664 /** 665 * add middleware to create Exceptions for responses with error status code 666 * @param errorMiddleware {@link ErrorMiddleware} to be used 667 * @return ClientBuilder instance 668 */ 669 public ClientBuilder withErrorMiddleware(final Supplier<ErrorMiddleware> errorMiddleware) { 670 this.errorMiddleware = errorMiddleware; 671 return this; 672 } 673 674 /** 675 * add middleware to create Exceptions for responses with error status code 676 * @return ClientBuilder instance 677 */ 678 public ClientBuilder withErrorMiddleware() { 679 return withErrorMiddleware(() -> ErrorMiddleware.of(httpExceptionFactory.get())); 680 } 681 682 /** 683 * add middleware to create Exceptions for responses with error status code 684 * @param errorMiddleware {@link ErrorMiddleware} to be used 685 * @return ClientBuilder instance 686 */ 687 public ClientBuilder withErrorMiddleware(final ErrorMiddleware errorMiddleware) { 688 return withErrorMiddleware(() -> errorMiddleware); 689 } 690 691 /** 692 * add middleware to create Exceptions for responses with error status code 693 * @param exceptionMode either use CompletionExceptions or unwrap the Exception. See {@link ErrorMiddleware.ExceptionMode} 694 * @return ClientBuilder instance 695 */ 696 public ClientBuilder withErrorMiddleware(ErrorMiddleware.ExceptionMode exceptionMode) { 697 return withErrorMiddleware(() -> ErrorMiddleware.of(httpExceptionFactory.get(), exceptionMode)); 698 } 699 700 /** 701 * add middleware to collect and report telemetry data 702 * @param telemetryMiddleware {@link TelemetryMiddleware} to be used 703 * @return ClientBuilder instance 704 */ 705 public ClientBuilder withTelemetryMiddleware(final Supplier<TelemetryMiddleware> telemetryMiddleware) { 706 this.telemetryMiddleware = telemetryMiddleware; 707 return this; 708 } 709 710 /** 711 * add middleware to collect and report telemetry data 712 * @param telemetryMiddleware {@link TelemetryMiddleware} to be used 713 * @return ClientBuilder instance 714 */ 715 public ClientBuilder withTelemetryMiddleware(final TelemetryMiddleware telemetryMiddleware) { 716 return withTelemetryMiddleware(() -> telemetryMiddleware); 717 } 718 719 /** 720 * add Middleware to convert a {@link io.vrap.rmf.base.client.error.NotFoundException} to a response with a null body value 721 * @return ClientBuilder instance 722 */ 723 public ClientBuilder addNotFoundExceptionMiddleware() { 724 return addNotFoundExceptionMiddleware(NotFoundExceptionMiddleware.of()); 725 } 726 727 /** 728 * add Middleware to convert a {@link io.vrap.rmf.base.client.error.NotFoundException} to a response with a null body value 729 * @param methods HTTP methods to convert on {@link io.vrap.rmf.base.client.error.NotFoundException} 730 * @return ClientBuilder instance 731 */ 732 public ClientBuilder addNotFoundExceptionMiddleware(final Set<ApiHttpMethod> methods) { 733 return addNotFoundExceptionMiddleware(NotFoundExceptionMiddleware.of(methods)); 734 } 735 736 /** 737 * add Middleware to convert a {@link io.vrap.rmf.base.client.error.NotFoundException} to a response with a null body value 738 * @param requestPredicate predicate to match for converting {@link io.vrap.rmf.base.client.error.NotFoundException} 739 * @return ClientBuilder instance 740 */ 741 public ClientBuilder addNotFoundExceptionMiddleware(final Predicate<ApiHttpRequest> requestPredicate) { 742 return addNotFoundExceptionMiddleware(NotFoundExceptionMiddleware.of(requestPredicate)); 743 } 744 745 /** 746 * add Middleware to convert a {@link io.vrap.rmf.base.client.error.NotFoundException} to a response with a null body value 747 * @param exceptionMiddleware middleware to be used 748 * @return ClientBuilder instance 749 */ 750 public ClientBuilder addNotFoundExceptionMiddleware(final NotFoundExceptionMiddleware exceptionMiddleware) { 751 return addMiddleware(exceptionMiddleware); 752 } 753 754 /** 755 * add middleware to retry failed requests 756 * @param retryMiddleware {@link RetryMiddleware} to be used 757 * @return ClientBuilder instance 758 */ 759 public ClientBuilder withRetryMiddleware(final Supplier<RetryRequestMiddleware> retryMiddleware) { 760 this.retryMiddleware = retryMiddleware; 761 return this; 762 } 763 764 /** 765 * add middleware to retry failed requests 766 * @param retryMiddleware {@link RetryMiddleware} to be used 767 * @return ClientBuilder instance 768 */ 769 public ClientBuilder withRetryMiddleware(final RetryRequestMiddleware retryMiddleware) { 770 return withRetryMiddleware(() -> retryMiddleware); 771 } 772 773 /** 774 * add middleware to retry failed requests. By default, status code 500 & 503 will be retried. Between each retry 775 * an incremental backoff strategy is applied 776 * @param maxRetries number of retries before giving uo 777 * @return ClientBuilder instance 778 */ 779 public ClientBuilder withRetryMiddleware(final int maxRetries) { 780 return withRetryMiddleware(RetryRequestMiddleware.of(maxRetries)); 781 } 782 783 /** 784 * add middleware to retry failed requests. Between each retry an incremental backoff strategy is applied 785 * @param maxRetries number of retries before giving uo 786 * @param statusCodes HTTP status codes to retry a failed request e.g. 500 & 503 787 * @return ClientBuilder instance 788 */ 789 public ClientBuilder withRetryMiddleware(final int maxRetries, List<Integer> statusCodes) { 790 return withRetryMiddleware(RetryRequestMiddleware.of(maxRetries, statusCodes)); 791 } 792 793 /** 794 * add middleware to retry failed requests. Between each retry an incremental backoff strategy is applied 795 * @param maxRetries number of retries before giving uo 796 * @param statusCodes HTTP status codes to retry a failed request e.g. 500 & 503 797 * @param failures {@link Throwable}s to be retried 798 * @return ClientBuilder instance 799 */ 800 public ClientBuilder withRetryMiddleware(final int maxRetries, List<Integer> statusCodes, 801 final List<Class<? extends Throwable>> failures) { 802 return withRetryMiddleware(RetryRequestMiddleware.of(maxRetries, statusCodes, failures)); 803 } 804 805 /** 806 * add middleware to retry failed requests. Between each retry an incremental backoff strategy is applied 807 * @param maxRetries number of retries before giving uo 808 * @param delay the initial delay for a retry 809 * @param maxDelay the maximum delay between each retry 810 * @param statusCodes HTTP status codes to retry a failed request e.g. 500 & 503 811 * @param failures {@link Throwable}s to be retried 812 * @param fn additional configuration for the {@link dev.failsafe.RetryPolicy} to be applied 813 * @return ClientBuilder instance 814 */ 815 public ClientBuilder withRetryMiddleware(final int maxRetries, final long delay, final long maxDelay, 816 List<Integer> statusCodes, final List<Class<? extends Throwable>> failures, 817 final FailsafeRetryPolicyBuilderOptions fn) { 818 return withRetryMiddleware( 819 RetryRequestMiddleware.of(maxRetries, delay, maxDelay, RetryRequestMiddleware.handleFailures(failures) 820 .andThen(RetryRequestMiddleware.handleStatusCodes(statusCodes).andThen(fn)))); 821 } 822 823 /** 824 * add middleware to retry failed requests. Between each retry an incremental backoff strategy is applied 825 * @param maxRetries number of retries before giving uo 826 * @param delay the initial delay for a retry 827 * @param maxDelay the maximum delay between each retry 828 * @param fn additional configuration for the {@link dev.failsafe.RetryPolicy} to be applied 829 * @return ClientBuilder instance 830 */ 831 public ClientBuilder withRetryMiddleware(final int maxRetries, final long delay, final long maxDelay, 832 final FailsafeRetryPolicyBuilderOptions fn) { 833 return withRetryMiddleware(RetryRequestMiddleware.of(maxRetries, delay, maxDelay, fn)); 834 } 835 836 /** 837 * add middleware to retry failed requests. Between each retry an incremental backoff strategy is applied 838 * @param executorService {@link ExecutorService} to be used for the retry handler 839 * @param maxRetries number of retries before giving uo 840 * @return ClientBuilder instance 841 */ 842 public ClientBuilder withRetryMiddleware(final ExecutorService executorService, final int maxRetries) { 843 return withRetryMiddleware(RetryRequestMiddleware.of(executorService, maxRetries)); 844 } 845 846 /** 847 * add middleware to retry failed requests. Between each retry an incremental backoff strategy is applied 848 * @param executorService {@link ExecutorService} to be used for the retry handler 849 * @param maxRetries number of retries before giving uo 850 * @param statusCodes HTTP status codes to retry a failed request e.g. 500 & 503 851 * @return ClientBuilder instance 852 */ 853 public ClientBuilder withRetryMiddleware(final ExecutorService executorService, final int maxRetries, 854 List<Integer> statusCodes) { 855 return withRetryMiddleware(RetryRequestMiddleware.of(executorService, maxRetries, statusCodes)); 856 } 857 858 /** 859 * add middleware to retry failed requests. Between each retry an incremental backoff strategy is applied 860 * @param executorService {@link ExecutorService} to be used for the retry handler 861 * @param maxRetries number of retries before giving uo 862 * @param statusCodes HTTP status codes to retry a failed request e.g. 500 & 503 863 * @param failures {@link Throwable}s to be retried 864 * @return ClientBuilder instance 865 */ 866 public ClientBuilder withRetryMiddleware(final ExecutorService executorService, final int maxRetries, 867 List<Integer> statusCodes, final List<Class<? extends Throwable>> failures) { 868 return withRetryMiddleware(RetryRequestMiddleware.of(executorService, maxRetries, statusCodes, failures)); 869 } 870 871 /** 872 * add middleware to retry failed requests. Between each retry an incremental backoff strategy is applied 873 * @param executorService {@link ExecutorService} to be used for the retry handler 874 * @param maxRetries number of retries before giving uo 875 * @param delay the initial delay for a retry 876 * @param maxDelay the maximum delay between each retry 877 * @param statusCodes HTTP status codes to retry a failed request e.g. 500 & 503 878 * @param failures {@link Throwable}s to be retried 879 * @param fn additional configuration for the {@link dev.failsafe.RetryPolicy} to be applied 880 * @return ClientBuilder instance 881 */ 882 public ClientBuilder withRetryMiddleware(final ExecutorService executorService, final int maxRetries, 883 final long delay, final long maxDelay, List<Integer> statusCodes, 884 final List<Class<? extends Throwable>> failures, final FailsafeRetryPolicyBuilderOptions fn) { 885 return withRetryMiddleware(RetryRequestMiddleware.of(executorService, maxRetries, delay, maxDelay, 886 RetryRequestMiddleware.handleFailures(failures) 887 .andThen(RetryRequestMiddleware.handleStatusCodes(statusCodes).andThen(fn)))); 888 } 889 890 /** 891 * add middleware to retry failed requests. Between each retry an incremental backoff strategy is applied 892 * @param executorService {@link ExecutorService} to be used for the retry handler 893 * @param maxRetries number of retries before giving uo 894 * @param delay the initial delay for a retry 895 * @param maxDelay the maximum delay between each retry 896 * @param fn additional configuration for the {@link dev.failsafe.RetryPolicy} to be applied 897 * @return ClientBuilder instance 898 */ 899 public ClientBuilder withRetryMiddleware(final ExecutorService executorService, final int maxRetries, 900 final long delay, final long maxDelay, final FailsafeRetryPolicyBuilderOptions fn) { 901 return withRetryMiddleware(RetryRequestMiddleware.of(executorService, maxRetries, delay, maxDelay, fn)); 902 } 903 904 /** 905 * add middleware to retry failed requests. Between each retry an incremental backoff strategy is applied 906 * @param executorService {@link ScheduledExecutorService} to be used for the retry handler 907 * @param maxRetries number of retries before giving uo 908 * @return ClientBuilder instance 909 */ 910 public ClientBuilder withRetryMiddleware(final ScheduledExecutorService executorService, final int maxRetries) { 911 return withRetryMiddleware(RetryRequestMiddleware.of(executorService, maxRetries)); 912 } 913 914 /** 915 * add middleware to retry failed requests. Between each retry an incremental backoff strategy is applied 916 * @param executorService {@link ScheduledExecutorService} to be used for the retry handler 917 * @param maxRetries number of retries before giving uo 918 * @param statusCodes HTTP status codes to retry a failed request e.g. 500 & 503 919 * @return ClientBuilder instance 920 */ 921 public ClientBuilder withRetryMiddleware(final ScheduledExecutorService executorService, final int maxRetries, 922 List<Integer> statusCodes) { 923 return withRetryMiddleware(RetryRequestMiddleware.of(executorService, maxRetries, statusCodes)); 924 } 925 926 /** 927 * add middleware to retry failed requests. Between each retry an incremental backoff strategy is applied 928 * @param executorService {@link ScheduledExecutorService} to be used for the retry handler 929 * @param maxRetries number of retries before giving uo 930 * @param statusCodes HTTP status codes to retry a failed request e.g. 500 & 503 931 * @param failures {@link Throwable}s to be retried 932 * @return ClientBuilder instance 933 */ 934 public ClientBuilder withRetryMiddleware(final ScheduledExecutorService executorService, final int maxRetries, 935 List<Integer> statusCodes, final List<Class<? extends Throwable>> failures) { 936 return withRetryMiddleware(RetryRequestMiddleware.of(executorService, maxRetries, statusCodes, failures)); 937 } 938 939 /** 940 * add middleware to retry failed requests. Between each retry an incremental backoff strategy is applied 941 * @param executorService {@link ScheduledExecutorService} to be used for the retry handler 942 * @param maxRetries number of retries before giving uo 943 * @param delay the initial delay for a retry 944 * @param maxDelay the maximum delay between each retry 945 * @param statusCodes HTTP status codes to retry a failed request e.g. 500 & 503 946 * @param failures {@link Throwable}s to be retried 947 * @param fn additional configuration for the {@link dev.failsafe.RetryPolicy} to be applied 948 * @return ClientBuilder instance 949 */ 950 public ClientBuilder withRetryMiddleware(final ScheduledExecutorService executorService, final int maxRetries, 951 final long delay, final long maxDelay, List<Integer> statusCodes, 952 final List<Class<? extends Throwable>> failures, final FailsafeRetryPolicyBuilderOptions fn) { 953 return withRetryMiddleware(RetryRequestMiddleware.of(executorService, maxRetries, delay, maxDelay, 954 RetryRequestMiddleware.handleFailures(failures) 955 .andThen(RetryRequestMiddleware.handleStatusCodes(statusCodes).andThen(fn)))); 956 } 957 958 /** 959 * add middleware to retry failed requests. Between each retry an incremental backoff strategy is applied 960 * @param executorService {@link ScheduledExecutorService} to be used for the retry handler 961 * @param maxRetries number of retries before giving uo 962 * @param delay the initial delay for a retry 963 * @param maxDelay the maximum delay between each retry 964 * @param fn additional configuration for the {@link dev.failsafe.RetryPolicy} to be applied 965 * @return ClientBuilder instance 966 */ 967 public ClientBuilder withRetryMiddleware(final ScheduledExecutorService executorService, final int maxRetries, 968 final long delay, final long maxDelay, final FailsafeRetryPolicyBuilderOptions fn) { 969 return withRetryMiddleware(RetryRequestMiddleware.of(executorService, maxRetries, delay, maxDelay, fn)); 970 } 971 972 /** 973 * add middleware to retry failed requests. Between each retry an incremental backoff strategy is applied 974 * @param scheduler {@link Scheduler} to be used for the retry handler 975 * @param maxRetries number of retries before giving uo 976 * @return ClientBuilder instance 977 */ 978 public ClientBuilder withRetryMiddleware(final Scheduler scheduler, final int maxRetries) { 979 return withRetryMiddleware(RetryRequestMiddleware.of(scheduler, maxRetries)); 980 } 981 982 /** 983 * add middleware to retry failed requests. Between each retry an incremental backoff strategy is applied 984 * @param scheduler {@link Scheduler} to be used for the retry handler 985 * @param maxRetries number of retries before giving uo 986 * @param statusCodes HTTP status codes to retry a failed request e.g. 500 & 503 987 * @return ClientBuilder instance 988 */ 989 public ClientBuilder withRetryMiddleware(final Scheduler scheduler, final int maxRetries, 990 List<Integer> statusCodes) { 991 return withRetryMiddleware(RetryRequestMiddleware.of(scheduler, maxRetries, statusCodes)); 992 } 993 994 /** 995 * add middleware to retry failed requests. Between each retry an incremental backoff strategy is applied 996 * @param scheduler {@link Scheduler} to be used for the retry handler 997 * @param maxRetries number of retries before giving uo 998 * @param statusCodes HTTP status codes to retry a failed request e.g. 500 & 503 999 * @param failures {@link Throwable}s to be retried 1000 * @return ClientBuilder instance 1001 */ 1002 public ClientBuilder withRetryMiddleware(final Scheduler scheduler, final int maxRetries, List<Integer> statusCodes, 1003 final List<Class<? extends Throwable>> failures) { 1004 return withRetryMiddleware(RetryRequestMiddleware.of(scheduler, maxRetries, statusCodes, failures)); 1005 } 1006 1007 /** 1008 * add middleware to retry failed requests. Between each retry an incremental backoff strategy is applied 1009 * @param scheduler {@link Scheduler} to be used for the retry handler 1010 * @param maxRetries number of retries before giving uo 1011 * @param delay the initial delay for a retry 1012 * @param maxDelay the maximum delay between each retry 1013 * @param statusCodes HTTP status codes to retry a failed request e.g. 500 & 503 1014 * @param failures {@link Throwable}s to be retried 1015 * @param fn additional configuration for the {@link dev.failsafe.RetryPolicy} to be applied 1016 * @return ClientBuilder instance 1017 */ 1018 public ClientBuilder withRetryMiddleware(final Scheduler scheduler, final int maxRetries, final long delay, 1019 final long maxDelay, List<Integer> statusCodes, final List<Class<? extends Throwable>> failures, 1020 final FailsafeRetryPolicyBuilderOptions fn) { 1021 return withRetryMiddleware(RetryRequestMiddleware.of(scheduler, maxRetries, delay, maxDelay, 1022 RetryRequestMiddleware.handleFailures(failures) 1023 .andThen(RetryRequestMiddleware.handleStatusCodes(statusCodes).andThen(fn)))); 1024 } 1025 1026 /** 1027 * add middleware to retry failed requests. Between each retry an incremental backoff strategy is applied 1028 * @param scheduler {@link Scheduler} to be used for the retry handler 1029 * @param maxRetries number of retries before giving uo 1030 * @param delay the initial delay for a retry 1031 * @param maxDelay the maximum delay between each retry 1032 * @param fn additional configuration for the {@link dev.failsafe.RetryPolicy} to be applied 1033 * @return ClientBuilder instance 1034 */ 1035 public ClientBuilder withRetryMiddleware(final Scheduler scheduler, final int maxRetries, final long delay, 1036 final long maxDelay, final FailsafeRetryPolicyBuilderOptions fn) { 1037 return withRetryMiddleware(RetryRequestMiddleware.of(scheduler, maxRetries, delay, maxDelay, fn)); 1038 } 1039 1040 /** 1041 * add middleware to limit the concurrent requests to be executed 1042 * @param queueMiddleware {@link QueueRequestMiddleware} to be used 1043 * @return ClientBuilder instance 1044 */ 1045 public ClientBuilder withQueueMiddleware(final Supplier<QueueRequestMiddleware> queueMiddleware) { 1046 this.queueMiddleware = queueMiddleware; 1047 return this; 1048 } 1049 1050 /** 1051 * add middleware to limit the concurrent requests to be executed 1052 * @param queueMiddleware {@link QueueRequestMiddleware} to be used 1053 * @return ClientBuilder instance 1054 */ 1055 public ClientBuilder withQueueMiddleware(final QueueRequestMiddleware queueMiddleware) { 1056 return withQueueMiddleware(() -> queueMiddleware); 1057 } 1058 1059 /** 1060 * add middleware to limit the concurrent requests to be executed 1061 * @param maxRequests maximum number of concurrent requests 1062 * @param maxWaitTime maximum time to wait before giving up 1063 * @return ClientBuilder instance 1064 */ 1065 public ClientBuilder withQueueMiddleware(final int maxRequests, final Duration maxWaitTime) { 1066 return withQueueMiddleware(() -> QueueRequestMiddleware.of(maxRequests, maxWaitTime)); 1067 } 1068 1069 /** 1070 * add middleware to limit the concurrent requests to be executed 1071 * @param scheduler {@link Scheduler} to be used for handling the queue 1072 * @param maxRequests maximum number of concurrent requests 1073 * @param maxWaitTime maximum time to wait before giving up 1074 * @return ClientBuilder instance 1075 */ 1076 public ClientBuilder withQueueMiddleware(final Scheduler scheduler, final int maxRequests, 1077 final Duration maxWaitTime) { 1078 return withQueueMiddleware(() -> QueueRequestMiddleware.of(scheduler, maxRequests, maxWaitTime)); 1079 } 1080 1081 /** 1082 * add middleware to limit the concurrent requests to be executed 1083 * @param executorService {@link ScheduledExecutorService} to be used for handling the queue 1084 * @param maxRequests maximum number of concurrent requests 1085 * @param maxWaitTime maximum time to wait before giving up 1086 * @return ClientBuilder instance 1087 */ 1088 public ClientBuilder withQueueMiddleware(final ScheduledExecutorService executorService, final int maxRequests, 1089 final Duration maxWaitTime) { 1090 return withQueueMiddleware(() -> QueueRequestMiddleware.of(executorService, maxRequests, maxWaitTime)); 1091 } 1092 1093 /** 1094 * add middleware to limit the concurrent requests to be executed 1095 * @param executorService {@link ExecutorService} to be used for handling the queue 1096 * @param maxRequests maximum number of concurrent requests 1097 * @param maxWaitTime maximum time to wait before giving up 1098 * @return ClientBuilder instance 1099 */ 1100 public ClientBuilder withQueueMiddleware(final ExecutorService executorService, final int maxRequests, 1101 final Duration maxWaitTime) { 1102 return withQueueMiddleware(() -> QueueRequestMiddleware.of(executorService, maxRequests, maxWaitTime)); 1103 } 1104 1105 /** 1106 * add authenticator middleware 1107 * @param oAuthMiddleware {@link OAuthMiddleware} to be used for authentication 1108 * @return ClientBuilder instance 1109 */ 1110 public ClientBuilder withOAuthMiddleware(final Supplier<OAuthMiddleware> oAuthMiddleware) { 1111 this.oAuthMiddleware = oAuthMiddleware; 1112 return this; 1113 } 1114 1115 /** 1116 * add authenticator middleware 1117 * @param oAuthMiddleware {@link OAuthMiddleware} to be used for authentication 1118 * @return ClientBuilder instance 1119 */ 1120 public ClientBuilder withOAuthMiddleware(final OAuthMiddleware oAuthMiddleware) { 1121 return withOAuthMiddleware(() -> oAuthMiddleware); 1122 } 1123 1124 /** 1125 * use supplier for authentication tokens 1126 * @param tokenSupplier {@link TokenSupplier} for retrieving authentication tokens 1127 * @return ClientBuilder instance 1128 */ 1129 public ClientBuilder withTokenSupplier(final Supplier<TokenSupplier> tokenSupplier) { 1130 return withOAuthMiddleware(() -> { 1131 final OAuthHandler oAuthHandler = new OAuthHandler(tokenSupplier.get()); 1132 return Optional.ofNullable(oauthExecutorService) 1133 .map(executorService -> OAuthMiddleware.of(executorService.get(), oAuthHandler, authRetries, 1134 useAuthCircuitBreaker)) 1135 .orElseGet(() -> OAuthMiddleware.of(oAuthHandler, authRetries, useAuthCircuitBreaker)); 1136 }); 1137 } 1138 1139 /** 1140 * use supplier for authentication tokens 1141 * @param tokenSupplier {@link TokenSupplier} for retrieving authentication tokens 1142 * @return ClientBuilder instance 1143 */ 1144 public ClientBuilder withTokenSupplier(final TokenSupplier tokenSupplier) { 1145 return withTokenSupplier(() -> tokenSupplier); 1146 } 1147 1148 /** 1149 * @param internalLoggerMiddleware {@link InternalLoggerMiddleware} used for logging requests and responses 1150 * @return ClientBuilder instance 1151 */ 1152 public ClientBuilder withInternalLoggerMiddleware(final InternalLoggerMiddleware internalLoggerMiddleware) { 1153 this.internalLoggerMiddleware = internalLoggerMiddleware; 1154 return this; 1155 } 1156 1157 /** 1158 * @param internalLoggerFactory {@link InternalLoggerFactory} creates the logger for request & responses 1159 * @return ClientBuilder instance 1160 */ 1161 public ClientBuilder withInternalLoggerFactory(final InternalLoggerFactory internalLoggerFactory) { 1162 return withInternalLoggerMiddleware(InternalLoggerMiddleware.of(internalLoggerFactory)); 1163 } 1164 1165 /** 1166 * @param internalLoggerFactory {@link InternalLoggerFactory} creates the logger for request & responses 1167 * @param responseLogEvent {@link Level} for logging responses. 1168 * @param deprecationLogEvent {@link Level} for logging {@link ApiHttpHeaders#X_DEPRECATION_NOTICE} 1169 * @return ClientBuilder instance 1170 */ 1171 public ClientBuilder withInternalLoggerFactory(final InternalLoggerFactory internalLoggerFactory, 1172 final Level responseLogEvent, final Level deprecationLogEvent) { 1173 return withInternalLoggerMiddleware( 1174 InternalLoggerMiddleware.of(internalLoggerFactory, responseLogEvent, deprecationLogEvent)); 1175 } 1176 1177 /** 1178 * @param internalLoggerFactory {@link InternalLoggerFactory} creates the logger for request & responses 1179 * @param responseLogEvent {@link Level} for logging responses. 1180 * @param deprecationLogEvent {@link Level} for logging {@link ApiHttpHeaders#X_DEPRECATION_NOTICE} 1181 * @param defaultExceptionLogEvent {@link Level} for logging errors 1182 * @param exceptionLogEvents {@link Level} for logging by exception class 1183 * @return ClientBuilder instance 1184 */ 1185 public ClientBuilder withInternalLoggerFactory(final InternalLoggerFactory internalLoggerFactory, 1186 final Level responseLogEvent, final Level deprecationLogEvent, final Level defaultExceptionLogEvent, 1187 final Map<Class<? extends Throwable>, Level> exceptionLogEvents) { 1188 return withInternalLoggerMiddleware(InternalLoggerMiddleware.of(internalLoggerFactory, responseLogEvent, 1189 deprecationLogEvent, defaultExceptionLogEvent, exceptionLogEvents)); 1190 } 1191 1192 /** 1193 * @param apiBaseUrl base URI for calling the API 1194 * @return ClientBuilder instance 1195 */ 1196 public ClientBuilder withApiBaseUrl(String apiBaseUrl) { 1197 return withApiBaseUrl(URI.create(apiBaseUrl)); 1198 } 1199 1200 /** 1201 * @param apiBaseUrl base URI for calling the API 1202 * @return ClientBuilder instance 1203 */ 1204 public ClientBuilder withApiBaseUrl(final URI apiBaseUrl) { 1205 if (!apiBaseUrl.getPath().endsWith("/")) { 1206 this.apiBaseUrl = URI.create(apiBaseUrl + "/"); 1207 return this; 1208 } 1209 this.apiBaseUrl = apiBaseUrl; 1210 return this; 1211 } 1212 1213 /** 1214 * @param userAgentSupplier user agent to be send with the requests 1215 * @return ClientBuilder instance 1216 */ 1217 public ClientBuilder withUserAgentSupplier(final Supplier<String> userAgentSupplier) { 1218 return withUserAgentMiddleware(new UserAgentMiddleware(userAgentSupplier.get())); 1219 } 1220 1221 private ClientBuilder withUserAgentMiddleware(final UserAgentMiddleware userAgentMiddleware) { 1222 this.userAgentMiddleware = userAgentMiddleware; 1223 return this; 1224 } 1225 1226 /** 1227 * @param correlationIdProvider provider to create correlation IDs for each request 1228 * @param replace replace any existing correlation id provider 1229 * @return ClientBuilder instance 1230 */ 1231 public ClientBuilder addCorrelationIdProvider(final @Nullable CorrelationIdProvider correlationIdProvider, 1232 final boolean replace) { 1233 if (!replace && correlationIdMiddleware != null) { 1234 return this; 1235 } 1236 if (correlationIdProvider != null) { 1237 correlationIdMiddleware = () -> (request, next) -> { 1238 if (request.getHeaders().getFirst(ApiHttpHeaders.X_CORRELATION_ID) != null) { 1239 return next.apply(request); 1240 } 1241 return next.apply( 1242 request.withHeader(ApiHttpHeaders.X_CORRELATION_ID, correlationIdProvider.getCorrelationId())); 1243 }; 1244 } 1245 return this; 1246 } 1247 1248 /** 1249 * @param correlationIdProvider provider to create correlation IDs for each request 1250 * @return ClientBuilder instance 1251 */ 1252 public ClientBuilder addCorrelationIdProvider(final @Nullable CorrelationIdProvider correlationIdProvider) { 1253 return addCorrelationIdProvider(correlationIdProvider, true); 1254 } 1255 1256 /** 1257 * sets the middlewares to be configured for the client. 1258 * @param middlewares {@link Middleware} instances 1259 * @return ClientBuilder instance 1260 */ 1261 public ClientBuilder withMiddlewares(final List<Middleware> middlewares) { 1262 this.middlewares = new ArrayList<>(middlewares); 1263 return this; 1264 } 1265 1266 /** 1267 * sets the middlewares to be configured for the client. 1268 * @param middleware {@link Middleware} instance 1269 * @param middlewares {@link Middleware} instances 1270 * @return ClientBuilder instance 1271 */ 1272 public ClientBuilder withMiddleware(final Middleware middleware, final Middleware... middlewares) { 1273 this.middlewares = new ArrayList<>(Collections.singletonList(middleware)); 1274 if (middlewares.length > 0) { 1275 this.middlewares.addAll(Arrays.asList(middlewares)); 1276 } 1277 return this; 1278 } 1279 1280 /** 1281 * adds the middlewares to be configured for the client. 1282 * @param middlewares {@link Middleware} instances 1283 * @return ClientBuilder instance 1284 */ 1285 public ClientBuilder addMiddlewares(final List<Middleware> middlewares) { 1286 this.middlewares.addAll(middlewares); 1287 return this; 1288 } 1289 1290 /** 1291 * adds the middlewares to be configured for the client. 1292 * @param middleware {@link Middleware} instance 1293 * @param middlewares {@link Middleware} instances 1294 * @return ClientBuilder instance 1295 */ 1296 public ClientBuilder addMiddleware(final Middleware middleware, final Middleware... middlewares) { 1297 this.middlewares.add(middleware); 1298 if (middlewares.length > 0) { 1299 this.middlewares.addAll(Arrays.asList(middlewares)); 1300 } 1301 return this; 1302 } 1303 1304 /** 1305 * build the {@link ApiHttpClient} with the configured values 1306 * @return {@link ApiHttpClient} 1307 */ 1308 public ApiHttpClient build() { 1309 return ApiHttpClient.of(requireNonNull(apiBaseUrl), requireNonNull(stack.get()), 1310 requireNonNull(serializer.get())); 1311 } 1312 1313 /** 1314 * default user agent provider 1315 * @return user agent string 1316 */ 1317 public static String buildDefaultUserAgent() { 1318 return UserAgentUtils.obtainUserAgent(); 1319 } 1320}