AsyncApiClient.java
package com.guinetik.rr.api;
import com.guinetik.rr.RocketRestConfig;
import com.guinetik.rr.http.AsyncHttpClient;
import com.guinetik.rr.http.RocketClientFactory;
import com.guinetik.rr.request.RequestSpec;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
/**
* Asynchronous API client that returns {@link CompletableFuture} for non-blocking operations.
*
* <p>This client executes HTTP requests asynchronously using a configurable thread pool,
* allowing the calling thread to continue processing while waiting for responses.
* Ideal for high-throughput applications or when making multiple concurrent API calls.
*
* <h2>Basic Usage</h2>
* <pre class="language-java"><code>
* ExecutorService executor = Executors.newFixedThreadPool(4);
* AsyncApiClient client = new AsyncApiClient("https://api.example.com", config, executor);
*
* // Execute async request
* CompletableFuture<User> future = client.executeAsync(
* RequestBuilder.get("/users/1")
* .responseType(User.class)
* .build()
* );
*
* // Process result when ready
* future.thenAccept(user -> System.out.println("Got: " + user.getName()));
*
* // Don't forget to shutdown
* client.shutdown();
* </code></pre>
*
* <h2>Multiple Concurrent Requests</h2>
* <pre class="language-java"><code>
* CompletableFuture<User> user1 = client.executeAsync(getRequest("/users/1"));
* CompletableFuture<User> user2 = client.executeAsync(getRequest("/users/2"));
* CompletableFuture<User> user3 = client.executeAsync(getRequest("/users/3"));
*
* // Wait for all to complete
* CompletableFuture.allOf(user1, user2, user3)
* .thenRun(() -> System.out.println("All users loaded"));
* </code></pre>
*
* <h2>Error Handling</h2>
* <pre class="language-java"><code>
* client.executeAsync(request)
* .thenAccept(user -> processUser(user))
* .exceptionally(ex -> {
* System.err.println("Failed: " + ex.getMessage());
* return null;
* });
* </code></pre>
*
* @author guinetik <guinetik@gmail.com>
* @see AbstractApiClient
* @see DefaultApiClient
* @see FluentApiClient
* @since 1.0.0
*/
public class AsyncApiClient extends AbstractApiClient {
private final AsyncHttpClient asyncClient;
/**
* Creates a new AsyncApiClient with the specified base URL, configuration, and executor.
*
* @param baseUrl The base URL for API requests
* @param config The RocketRest configuration
* @param executor The ExecutorService to run requests on
*/
public AsyncApiClient(String baseUrl, RocketRestConfig config, ExecutorService executor) {
super(baseUrl, config, createAsyncHttpClient(baseUrl, config, executor));
this.asyncClient = (AsyncHttpClient) httpClient;
}
/**
* Creates a new AsyncApiClient with the specified base URL, configuration, and a
* pre-configured AsyncHttpClient instance.
*
* @param baseUrl The base URL for API requests
* @param config The RocketRest configuration
* @param asyncClient A pre-configured AsyncHttpClient instance
*/
public AsyncApiClient(String baseUrl, RocketRestConfig config, AsyncHttpClient asyncClient) {
super(baseUrl, config, asyncClient);
this.asyncClient = asyncClient;
}
/**
* Executes a request asynchronously.
*
* @param <Req> The type of the request
* @param <Res> The type of the response
* @param requestSpec The request specification
* @return A CompletableFuture that will complete with the response
*/
public <Req, Res> CompletableFuture<Res> executeAsync(RequestSpec<Req, Res> requestSpec) {
return asyncClient.executeAsync(requestSpec);
}
/**
* Shuts down the executor service.
*/
public void shutdown() {
asyncClient.shutdown();
}
/**
* Creates an AsyncHttpClient with the appropriate options.
*
* @param baseUrl The base URL for API requests
* @param config The RocketRest configuration
* @param executor The ExecutorService to run requests on
* @return A new AsyncHttpClient
*/
private static AsyncHttpClient createAsyncHttpClient(String baseUrl, RocketRestConfig config, ExecutorService executor) {
return RocketClientFactory.fromConfig(config)
.withExecutorService(executor)
.buildAsync();
}
}