Skip to content

Commit 7d3e5b3

Browse files
committed
feat: Add pluggable A2AHttpClient with Vert.x implementation
Introduce ServiceLoader-based A2AHttpClient client architecture with two implementations: - JDK HttpClient (core, always available) - Vert.x WebClient (optional, high-performance) Changes: - Create http-client-vertx module for Vert.x implementation - Add ServiceLoader infrastructure (A2AHttpClientFactory, providers) - Update dependent modules to use Vert.x client by default - Add comprehensive tests and usage examples Users can choose implementation via Maven dependencies. Priority-based selection: Vert.x (100) > JDK (0). Signed-off-by: Emmanuel Hugonnet <ehugonne@redhat.com>
1 parent cf82353 commit 7d3e5b3

File tree

33 files changed

+1767
-34
lines changed

33 files changed

+1767
-34
lines changed

boms/sdk/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,11 @@
4949
<artifactId>a2a-java-sdk-http-client</artifactId>
5050
<version>${project.version}</version>
5151
</dependency>
52+
<dependency>
53+
<groupId>${project.groupId}</groupId>
54+
<artifactId>a2a-java-sdk-http-client-vertx</artifactId>
55+
<version>${project.version}</version>
56+
</dependency>
5257

5358
<!-- Server modules -->
5459
<dependency>

client/base/pom.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@
2222
<groupId>${project.groupId}</groupId>
2323
<artifactId>a2a-java-sdk-http-client</artifactId>
2424
</dependency>
25+
<dependency>
26+
<groupId>${project.groupId}</groupId>
27+
<artifactId>a2a-java-sdk-http-client-vertx</artifactId>
28+
</dependency>
2529
<dependency>
2630
<groupId>${project.groupId}</groupId>
2731
<artifactId>a2a-java-sdk-client-transport-spi</artifactId>

client/base/src/main/java/io/a2a/A2A.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
import io.a2a.client.http.A2ACardResolver;
88
import io.a2a.client.http.A2AHttpClient;
9-
import io.a2a.client.http.JdkA2AHttpClient;
9+
import io.a2a.client.http.A2AHttpClientFactory;
1010
import io.a2a.spec.A2AClientError;
1111
import io.a2a.spec.A2AClientJSONError;
1212
import io.a2a.spec.AgentCard;
@@ -286,7 +286,7 @@ private static Message toMessage(List<Part<?>> parts, Message.Role role, String
286286
* @see AgentCard
287287
*/
288288
public static AgentCard getAgentCard(String agentUrl) throws A2AClientError, A2AClientJSONError {
289-
return getAgentCard(new JdkA2AHttpClient(), agentUrl);
289+
return getAgentCard(A2AHttpClientFactory.create(), agentUrl);
290290
}
291291

292292
/**
@@ -357,7 +357,7 @@ public static AgentCard getAgentCard(A2AHttpClient httpClient, String agentUrl)
357357
* @throws io.a2a.spec.A2AClientJSONError if the response body cannot be decoded as JSON or validated against the AgentCard schema
358358
*/
359359
public static AgentCard getAgentCard(String agentUrl, String relativeCardPath, Map<String, String> authHeaders) throws A2AClientError, A2AClientJSONError {
360-
return getAgentCard(new JdkA2AHttpClient(), agentUrl, relativeCardPath, authHeaders);
360+
return getAgentCard(A2AHttpClientFactory.create(), agentUrl, relativeCardPath, authHeaders);
361361
}
362362

363363
/**

client/base/src/test/java/io/a2a/client/ClientBuilderTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import java.util.List;
77

88
import io.a2a.client.config.ClientConfig;
9-
import io.a2a.client.http.JdkA2AHttpClient;
9+
import io.a2a.client.http.A2AHttpClientFactory;
1010
import io.a2a.client.transport.grpc.GrpcTransport;
1111
import io.a2a.client.transport.grpc.GrpcTransportConfigBuilder;
1212
import io.a2a.client.transport.jsonrpc.JSONRPCTransport;
@@ -89,7 +89,7 @@ public void shouldCreateClient_differentConfigurations() throws A2AClientExcepti
8989
Client client = Client
9090
.builder(card)
9191
.withTransport(JSONRPCTransport.class, new JSONRPCTransportConfigBuilder())
92-
.withTransport(JSONRPCTransport.class, new JSONRPCTransportConfig(new JdkA2AHttpClient()))
92+
.withTransport(JSONRPCTransport.class, new JSONRPCTransportConfig(A2AHttpClientFactory.create()))
9393
.build();
9494

9595
Assertions.assertNotNull(client);

client/transport/jsonrpc/pom.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@
2121
<groupId>${project.groupId}</groupId>
2222
<artifactId>a2a-java-sdk-http-client</artifactId>
2323
</dependency>
24+
<dependency>
25+
<groupId>${project.groupId}</groupId>
26+
<artifactId>a2a-java-sdk-http-client-vertx</artifactId>
27+
</dependency>
2428
<dependency>
2529
<groupId>${project.groupId}</groupId>
2630
<artifactId>a2a-java-sdk-client-transport-spi</artifactId>

client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@
2323
import com.google.protobuf.MessageOrBuilder;
2424
import io.a2a.client.http.A2ACardResolver;
2525
import io.a2a.client.http.A2AHttpClient;
26+
import io.a2a.client.http.A2AHttpClientFactory;
2627
import io.a2a.client.http.A2AHttpResponse;
27-
import io.a2a.client.http.JdkA2AHttpClient;
2828
import io.a2a.client.transport.jsonrpc.sse.SSEEventListener;
2929
import io.a2a.client.transport.spi.ClientTransport;
3030
import io.a2a.client.transport.spi.interceptors.ClientCallContext;
@@ -84,7 +84,7 @@ public JSONRPCTransport(AgentCard agentCard) {
8484

8585
public JSONRPCTransport(@Nullable A2AHttpClient httpClient, @Nullable AgentCard agentCard,
8686
AgentInterface agentInterface, @Nullable List<ClientCallInterceptor> interceptors) {
87-
this.httpClient = httpClient == null ? new JdkA2AHttpClient() : httpClient;
87+
this.httpClient = httpClient == null ? A2AHttpClientFactory.create() : httpClient;
8888
this.agentCard = agentCard;
8989
this.agentInterface = agentInterface;
9090
this.interceptors = interceptors;

client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportConfigBuilder.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package io.a2a.client.transport.jsonrpc;
22

33
import io.a2a.client.http.A2AHttpClient;
4-
import io.a2a.client.http.JdkA2AHttpClient;
4+
import io.a2a.client.http.A2AHttpClientFactory;
55
import io.a2a.client.transport.spi.ClientTransportConfigBuilder;
66
import org.jspecify.annotations.Nullable;
77

@@ -11,7 +11,7 @@
1111
* This builder provides a fluent API for configuring the JSON-RPC transport protocol.
1212
* All configuration options are optional - if not specified, sensible defaults are used:
1313
* <ul>
14-
* <li><b>HTTP client:</b> {@link JdkA2AHttpClient} (JDK's built-in HTTP client)</li>
14+
* <li><b>HTTP client:</b> Auto-selected via {@link A2AHttpClientFactory} (prefers Vert.x, falls back to JDK)</li>
1515
* <li><b>Interceptors:</b> None</li>
1616
* </ul>
1717
* <p>
@@ -78,7 +78,7 @@ public class JSONRPCTransportConfigBuilder extends ClientTransportConfigBuilder<
7878
* <li>Custom header handling</li>
7979
* </ul>
8080
* <p>
81-
* If not specified, the default {@link JdkA2AHttpClient} is used.
81+
* If not specified, a client is auto-selected via {@link A2AHttpClientFactory}.
8282
* <p>
8383
* Example:
8484
* <pre>{@code
@@ -101,16 +101,16 @@ public JSONRPCTransportConfigBuilder httpClient(A2AHttpClient httpClient) {
101101
/**
102102
* Build the JSON-RPC transport configuration.
103103
* <p>
104-
* If no HTTP client was configured, the default {@link JdkA2AHttpClient} is used.
104+
* If no HTTP client was configured, one is auto-selected via {@link A2AHttpClientFactory}.
105105
* Any configured interceptors are transferred to the configuration.
106106
*
107107
* @return the configured JSON-RPC transport configuration
108108
*/
109109
@Override
110110
public JSONRPCTransportConfig build() {
111-
// No HTTP client provided, fallback to the default one (JDK-based implementation)
111+
// No HTTP client provided, use factory to get best available implementation
112112
if (httpClient == null) {
113-
httpClient = new JdkA2AHttpClient();
113+
httpClient = A2AHttpClientFactory.create();
114114
}
115115

116116
JSONRPCTransportConfig config = new JSONRPCTransportConfig(httpClient);

client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportProvider.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package io.a2a.client.transport.jsonrpc;
22

3-
import io.a2a.client.http.JdkA2AHttpClient;
3+
import io.a2a.client.http.A2AHttpClientFactory;
44
import io.a2a.client.transport.spi.ClientTransportProvider;
55
import io.a2a.spec.A2AClientException;
66
import io.a2a.spec.AgentCard;
@@ -14,7 +14,7 @@ public class JSONRPCTransportProvider implements ClientTransportProvider<JSONRPC
1414
public JSONRPCTransport create(@Nullable JSONRPCTransportConfig clientTransportConfig, AgentCard agentCard, AgentInterface agentInterface) throws A2AClientException {
1515
JSONRPCTransportConfig currentClientTransportConfig = clientTransportConfig;
1616
if (currentClientTransportConfig == null) {
17-
currentClientTransportConfig = new JSONRPCTransportConfig(new JdkA2AHttpClient());
17+
currentClientTransportConfig = new JSONRPCTransportConfig(A2AHttpClientFactory.create());
1818
}
1919
return new JSONRPCTransport(currentClientTransportConfig.getHttpClient(), agentCard, agentInterface, currentClientTransportConfig.getInterceptors());
2020
}

client/transport/rest/pom.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@
4242
<groupId>io.github.a2asdk</groupId>
4343
<artifactId>a2a-java-sdk-http-client</artifactId>
4444
</dependency>
45+
<dependency>
46+
<groupId>io.github.a2asdk</groupId>
47+
<artifactId>a2a-java-sdk-http-client-vertx</artifactId>
48+
</dependency>
4549
<dependency>
4650
<groupId>com.google.protobuf</groupId>
4751
<artifactId>protobuf-java-util</artifactId>

client/transport/rest/src/main/java/io/a2a/client/transport/rest/RestTransport.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@
2929
import com.google.protobuf.util.JsonFormat;
3030
import io.a2a.client.http.A2ACardResolver;
3131
import io.a2a.client.http.A2AHttpClient;
32+
import io.a2a.client.http.A2AHttpClientFactory;
3233
import io.a2a.client.http.A2AHttpResponse;
33-
import io.a2a.client.http.JdkA2AHttpClient;
3434
import io.a2a.client.transport.rest.sse.RestSSEEventListener;
3535
import io.a2a.client.transport.spi.ClientTransport;
3636
import io.a2a.client.transport.spi.interceptors.ClientCallContext;
@@ -74,7 +74,7 @@ public RestTransport(AgentCard agentCard) {
7474

7575
public RestTransport(@Nullable A2AHttpClient httpClient, AgentCard agentCard,
7676
AgentInterface agentInterface, @Nullable List<ClientCallInterceptor> interceptors) {
77-
this.httpClient = httpClient == null ? new JdkA2AHttpClient() : httpClient;
77+
this.httpClient = httpClient == null ? A2AHttpClientFactory.create() : httpClient;
7878
this.agentCard = agentCard;
7979
this.agentInterface = agentInterface;
8080
this.interceptors = interceptors;

0 commit comments

Comments
 (0)