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