View Javadoc
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&lt;User&gt; 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 -&gt; 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&lt;User&gt; user1 = client.executeAsync(getRequest("/users/1"));
40   * CompletableFuture&lt;User&gt; user2 = client.executeAsync(getRequest("/users/2"));
41   * CompletableFuture&lt;User&gt; user3 = client.executeAsync(getRequest("/users/3"));
42   *
43   * // Wait for all to complete
44   * CompletableFuture.allOf(user1, user2, user3)
45   *     .thenRun(() -&gt; 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 -&gt; processUser(user))
52   *     .exceptionally(ex -&gt; {
53   *         System.err.println("Failed: " + ex.getMessage());
54   *         return null;
55   *     });
56   * </code></pre>
57   *
58   * @author guinetik &lt;guinetik@gmail.com&gt;
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 }