1 package com.guinetik.rr.api;
2
3 import com.guinetik.rr.RocketRestConfig;
4 import com.guinetik.rr.http.AsyncHttpClient;
5 import com.guinetik.rr.http.RocketClientFactory;
6 import com.guinetik.rr.request.RequestSpec;
7
8 import java.util.concurrent.CompletableFuture;
9 import java.util.concurrent.ExecutorService;
10
11 /**
12 * Asynchronous API client that returns {@link CompletableFuture} for non-blocking operations.
13 *
14 * <p>This client executes HTTP requests asynchronously using a configurable thread pool,
15 * allowing the calling thread to continue processing while waiting for responses.
16 * Ideal for high-throughput applications or when making multiple concurrent API calls.
17 *
18 * <h2>Basic Usage</h2>
19 * <pre class="language-java"><code>
20 * ExecutorService executor = Executors.newFixedThreadPool(4);
21 * AsyncApiClient client = new AsyncApiClient("https://api.example.com", config, executor);
22 *
23 * // Execute async request
24 * CompletableFuture<User> future = client.executeAsync(
25 * RequestBuilder.get("/users/1")
26 * .responseType(User.class)
27 * .build()
28 * );
29 *
30 * // Process result when ready
31 * future.thenAccept(user -> System.out.println("Got: " + user.getName()));
32 *
33 * // Don't forget to shutdown
34 * client.shutdown();
35 * </code></pre>
36 *
37 * <h2>Multiple Concurrent Requests</h2>
38 * <pre class="language-java"><code>
39 * CompletableFuture<User> user1 = client.executeAsync(getRequest("/users/1"));
40 * CompletableFuture<User> user2 = client.executeAsync(getRequest("/users/2"));
41 * CompletableFuture<User> user3 = client.executeAsync(getRequest("/users/3"));
42 *
43 * // Wait for all to complete
44 * CompletableFuture.allOf(user1, user2, user3)
45 * .thenRun(() -> System.out.println("All users loaded"));
46 * </code></pre>
47 *
48 * <h2>Error Handling</h2>
49 * <pre class="language-java"><code>
50 * client.executeAsync(request)
51 * .thenAccept(user -> processUser(user))
52 * .exceptionally(ex -> {
53 * System.err.println("Failed: " + ex.getMessage());
54 * return null;
55 * });
56 * </code></pre>
57 *
58 * @author guinetik <guinetik@gmail.com>
59 * @see AbstractApiClient
60 * @see DefaultApiClient
61 * @see FluentApiClient
62 * @since 1.0.0
63 */
64 public class AsyncApiClient extends AbstractApiClient {
65
66 private final AsyncHttpClient asyncClient;
67
68 /**
69 * Creates a new AsyncApiClient with the specified base URL, configuration, and executor.
70 *
71 * @param baseUrl The base URL for API requests
72 * @param config The RocketRest configuration
73 * @param executor The ExecutorService to run requests on
74 */
75 public AsyncApiClient(String baseUrl, RocketRestConfig config, ExecutorService executor) {
76 super(baseUrl, config, createAsyncHttpClient(baseUrl, config, executor));
77 this.asyncClient = (AsyncHttpClient) httpClient;
78 }
79
80 /**
81 * Creates a new AsyncApiClient with the specified base URL, configuration, and a
82 * pre-configured AsyncHttpClient instance.
83 *
84 * @param baseUrl The base URL for API requests
85 * @param config The RocketRest configuration
86 * @param asyncClient A pre-configured AsyncHttpClient instance
87 */
88 public AsyncApiClient(String baseUrl, RocketRestConfig config, AsyncHttpClient asyncClient) {
89 super(baseUrl, config, asyncClient);
90 this.asyncClient = asyncClient;
91 }
92
93 /**
94 * Executes a request asynchronously.
95 *
96 * @param <Req> The type of the request
97 * @param <Res> The type of the response
98 * @param requestSpec The request specification
99 * @return A CompletableFuture that will complete with the response
100 */
101 public <Req, Res> CompletableFuture<Res> executeAsync(RequestSpec<Req, Res> requestSpec) {
102 return asyncClient.executeAsync(requestSpec);
103 }
104
105 /**
106 * Shuts down the executor service.
107 */
108 public void shutdown() {
109 asyncClient.shutdown();
110 }
111
112 /**
113 * Creates an AsyncHttpClient with the appropriate options.
114 *
115 * @param baseUrl The base URL for API requests
116 * @param config The RocketRest configuration
117 * @param executor The ExecutorService to run requests on
118 * @return A new AsyncHttpClient
119 */
120 private static AsyncHttpClient createAsyncHttpClient(String baseUrl, RocketRestConfig config, ExecutorService executor) {
121 return RocketClientFactory.fromConfig(config)
122 .withExecutorService(executor)
123 .buildAsync();
124 }
125 }