Skip to content

Commit c7b95dd

Browse files
committed
chore: Using error prone and jspecify to define nullability on the
client API. Signed-off-by: Emmanuel Hugonnet <[email protected]>
1 parent 248c7c5 commit c7b95dd

File tree

11 files changed

+158
-85
lines changed

11 files changed

+158
-85
lines changed

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

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
import io.a2a.spec.TaskIdParams;
2020
import io.a2a.spec.TaskPushNotificationConfig;
2121
import io.a2a.spec.TaskQueryParams;
22+
import org.jspecify.annotations.NonNull;
23+
import org.jspecify.annotations.Nullable;
2224

2325
/**
2426
* Abstract class representing an A2A client. Provides a standard set
@@ -29,13 +31,13 @@
2931
public abstract class AbstractClient {
3032

3133
private final List<BiConsumer<ClientEvent, AgentCard>> consumers;
32-
private final Consumer<Throwable> streamingErrorHandler;
34+
private final @Nullable Consumer<Throwable> streamingErrorHandler;
3335

3436
public AbstractClient(List<BiConsumer<ClientEvent, AgentCard>> consumers) {
3537
this(consumers, null);
3638
}
3739

38-
public AbstractClient(List<BiConsumer<ClientEvent, AgentCard>> consumers, Consumer<Throwable> streamingErrorHandler) {
40+
public AbstractClient(@NonNull List<BiConsumer<ClientEvent, AgentCard>> consumers, @Nullable Consumer<Throwable> streamingErrorHandler) {
3941
checkNotNullParam("consumers", consumers);
4042
this.consumers = consumers;
4143
this.streamingErrorHandler = streamingErrorHandler;
@@ -70,7 +72,7 @@ public void sendMessage(Message request) throws A2AClientException {
7072
* @param context optional client call context for the request (may be {@code null})
7173
* @throws A2AClientException if sending the message fails for any reason
7274
*/
73-
public abstract void sendMessage(Message request, ClientCallContext context) throws A2AClientException;
75+
public abstract void sendMessage(Message request, @Nullable ClientCallContext context) throws A2AClientException;
7476

7577
/**
7678
* Send a message to the remote agent. This method will automatically use
@@ -110,7 +112,7 @@ public void sendMessage(Message request,
110112
public abstract void sendMessage(Message request,
111113
List<BiConsumer<ClientEvent, AgentCard>> consumers,
112114
Consumer<Throwable> streamingErrorHandler,
113-
ClientCallContext context) throws A2AClientException;
115+
@Nullable ClientCallContext context) throws A2AClientException;
114116

115117
/**
116118
* Send a message to the remote agent. This method will automatically use
@@ -147,7 +149,7 @@ public void sendMessage(Message request, PushNotificationConfig pushNotification
147149
* @throws A2AClientException if sending the message fails for any reason
148150
*/
149151
public abstract void sendMessage(Message request, PushNotificationConfig pushNotificationConfiguration,
150-
Map<String, Object> metadata, ClientCallContext context) throws A2AClientException;
152+
Map<String, Object> metadata, @Nullable ClientCallContext context) throws A2AClientException;
151153

152154
/**
153155
* Retrieve the current state and history of a specific task.
@@ -168,7 +170,7 @@ public Task getTask(TaskQueryParams request) throws A2AClientException {
168170
* @return the task
169171
* @throws A2AClientException if retrieving the task fails for any reason
170172
*/
171-
public abstract Task getTask(TaskQueryParams request, ClientCallContext context) throws A2AClientException;
173+
public abstract Task getTask(TaskQueryParams request, @Nullable ClientCallContext context) throws A2AClientException;
172174

173175
/**
174176
* Request the agent to cancel a specific task.
@@ -189,7 +191,7 @@ public Task cancelTask(TaskIdParams request) throws A2AClientException {
189191
* @return the cancelled task
190192
* @throws A2AClientException if cancelling the task fails for any reason
191193
*/
192-
public abstract Task cancelTask(TaskIdParams request, ClientCallContext context) throws A2AClientException;
194+
public abstract Task cancelTask(TaskIdParams request, @Nullable ClientCallContext context) throws A2AClientException;
193195

194196
/**
195197
* Set or update the push notification configuration for a specific task.
@@ -213,7 +215,7 @@ public TaskPushNotificationConfig setTaskPushNotificationConfiguration(
213215
*/
214216
public abstract TaskPushNotificationConfig setTaskPushNotificationConfiguration(
215217
TaskPushNotificationConfig request,
216-
ClientCallContext context) throws A2AClientException;
218+
@Nullable ClientCallContext context) throws A2AClientException;
217219

218220
/**
219221
* Retrieve the push notification configuration for a specific task.
@@ -237,7 +239,7 @@ public TaskPushNotificationConfig getTaskPushNotificationConfiguration(
237239
*/
238240
public abstract TaskPushNotificationConfig getTaskPushNotificationConfiguration(
239241
GetTaskPushNotificationConfigParams request,
240-
ClientCallContext context) throws A2AClientException;
242+
@Nullable ClientCallContext context) throws A2AClientException;
241243

242244
/**
243245
* Retrieve the list of push notification configurations for a specific task.
@@ -261,7 +263,7 @@ public List<TaskPushNotificationConfig> listTaskPushNotificationConfigurations(
261263
*/
262264
public abstract List<TaskPushNotificationConfig> listTaskPushNotificationConfigurations(
263265
ListTaskPushNotificationConfigParams request,
264-
ClientCallContext context) throws A2AClientException;
266+
@Nullable ClientCallContext context) throws A2AClientException;
265267

266268
/**
267269
* Delete the list of push notification configurations for a specific task.
@@ -283,7 +285,7 @@ public void deleteTaskPushNotificationConfigurations(
283285
*/
284286
public abstract void deleteTaskPushNotificationConfigurations(
285287
DeleteTaskPushNotificationConfigParams request,
286-
ClientCallContext context) throws A2AClientException;
288+
@Nullable ClientCallContext context) throws A2AClientException;
287289

288290
/**
289291
* Resubscribe to a task's event stream.
@@ -310,7 +312,7 @@ public void resubscribe(TaskIdParams request) throws A2AClientException {
310312
* @param context optional client call context for the request (may be {@code null})
311313
* @throws A2AClientException if resubscribing fails for any reason
312314
*/
313-
public abstract void resubscribe(TaskIdParams request, ClientCallContext context) throws A2AClientException;
315+
public abstract void resubscribe(TaskIdParams request, @Nullable ClientCallContext context) throws A2AClientException;
314316

315317
/**
316318
* Resubscribe to a task's event stream.
@@ -343,7 +345,7 @@ public void resubscribe(TaskIdParams request, List<BiConsumer<ClientEvent, Agent
343345
* @throws A2AClientException if resubscribing fails for any reason
344346
*/
345347
public abstract void resubscribe(TaskIdParams request, List<BiConsumer<ClientEvent, AgentCard>> consumers,
346-
Consumer<Throwable> streamingErrorHandler, ClientCallContext context) throws A2AClientException;
348+
Consumer<Throwable> streamingErrorHandler, @Nullable ClientCallContext context) throws A2AClientException;
347349

348350
/**
349351
* Retrieve the AgentCard.
@@ -362,7 +364,7 @@ public AgentCard getAgentCard() throws A2AClientException {
362364
* @return the AgentCard
363365
* @throws A2AClientException if retrieving the agent card fails for any reason
364366
*/
365-
public abstract AgentCard getAgentCard(ClientCallContext context) throws A2AClientException;
367+
public abstract AgentCard getAgentCard(@Nullable ClientCallContext context) throws A2AClientException;
366368

367369
/**
368370
* Close the transport and release any associated resources.
@@ -383,7 +385,7 @@ void consume(ClientEvent clientEventOrMessage, AgentCard agentCard) {
383385
*
384386
* @return the streaming error handler
385387
*/
386-
public Consumer<Throwable> getStreamingErrorHandler() {
388+
public @Nullable Consumer<Throwable> getStreamingErrorHandler() {
387389
return streamingErrorHandler;
388390
}
389391

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

Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,17 @@
3030

3131
import static io.a2a.util.Assert.checkNotNullParam;
3232

33+
import org.jspecify.annotations.NonNull;
34+
import org.jspecify.annotations.Nullable;
35+
3336
public class Client extends AbstractClient {
3437

3538
private final ClientConfig clientConfig;
3639
private final ClientTransport clientTransport;
3740
private AgentCard agentCard;
3841

3942
Client(AgentCard agentCard, ClientConfig clientConfig, ClientTransport clientTransport,
40-
List<BiConsumer<ClientEvent, AgentCard>> consumers, Consumer<Throwable> streamingErrorHandler) {
43+
List<BiConsumer<ClientEvent, AgentCard>> consumers, @Nullable Consumer<Throwable> streamingErrorHandler) {
4144
super(consumers, streamingErrorHandler);
4245
checkNotNullParam("agentCard", agentCard);
4346

@@ -51,21 +54,21 @@ public static ClientBuilder builder(AgentCard agentCard) {
5154
}
5255

5356
@Override
54-
public void sendMessage(Message request, ClientCallContext context) throws A2AClientException {
57+
public void sendMessage(Message request, @Nullable ClientCallContext context) throws A2AClientException {
5558
MessageSendParams messageSendParams = getMessageSendParams(request, clientConfig);
5659
sendMessage(messageSendParams, null, null, context);
5760
}
5861

5962
@Override
6063
public void sendMessage(Message request, List<BiConsumer<ClientEvent, AgentCard>> consumers,
61-
Consumer<Throwable> streamingErrorHandler, ClientCallContext context) throws A2AClientException {
64+
Consumer<Throwable> streamingErrorHandler, @Nullable ClientCallContext context) throws A2AClientException {
6265
MessageSendParams messageSendParams = getMessageSendParams(request, clientConfig);
6366
sendMessage(messageSendParams, consumers, streamingErrorHandler, context);
6467
}
6568

6669
@Override
6770
public void sendMessage(Message request, PushNotificationConfig pushNotificationConfiguration,
68-
Map<String, Object> metatadata, ClientCallContext context) throws A2AClientException {
71+
Map<String, Object> metatadata, @Nullable ClientCallContext context) throws A2AClientException {
6972
MessageSendConfiguration messageSendConfiguration = createMessageSendConfiguration(pushNotificationConfiguration);
7073

7174
MessageSendParams messageSendParams = new MessageSendParams.Builder()
@@ -78,52 +81,52 @@ public void sendMessage(Message request, PushNotificationConfig pushNotification
7881
}
7982

8083
@Override
81-
public Task getTask(TaskQueryParams request, ClientCallContext context) throws A2AClientException {
84+
public Task getTask(TaskQueryParams request, @Nullable ClientCallContext context) throws A2AClientException {
8285
return clientTransport.getTask(request, context);
8386
}
8487

8588
@Override
86-
public Task cancelTask(TaskIdParams request, ClientCallContext context) throws A2AClientException {
89+
public Task cancelTask(TaskIdParams request, @Nullable ClientCallContext context) throws A2AClientException {
8790
return clientTransport.cancelTask(request, context);
8891
}
8992

9093
@Override
9194
public TaskPushNotificationConfig setTaskPushNotificationConfiguration(
92-
TaskPushNotificationConfig request, ClientCallContext context) throws A2AClientException {
95+
TaskPushNotificationConfig request, @Nullable ClientCallContext context) throws A2AClientException {
9396
return clientTransport.setTaskPushNotificationConfiguration(request, context);
9497
}
9598

9699
@Override
97100
public TaskPushNotificationConfig getTaskPushNotificationConfiguration(
98-
GetTaskPushNotificationConfigParams request, ClientCallContext context) throws A2AClientException {
101+
GetTaskPushNotificationConfigParams request, @Nullable ClientCallContext context) throws A2AClientException {
99102
return clientTransport.getTaskPushNotificationConfiguration(request, context);
100103
}
101104

102105
@Override
103106
public List<TaskPushNotificationConfig> listTaskPushNotificationConfigurations(
104-
ListTaskPushNotificationConfigParams request, ClientCallContext context) throws A2AClientException {
107+
ListTaskPushNotificationConfigParams request, @Nullable ClientCallContext context) throws A2AClientException {
105108
return clientTransport.listTaskPushNotificationConfigurations(request, context);
106109
}
107110

108111
@Override
109112
public void deleteTaskPushNotificationConfigurations(
110-
DeleteTaskPushNotificationConfigParams request, ClientCallContext context) throws A2AClientException {
113+
DeleteTaskPushNotificationConfigParams request, @Nullable ClientCallContext context) throws A2AClientException {
111114
clientTransport.deleteTaskPushNotificationConfigurations(request, context);
112115
}
113116

114117
@Override
115-
public void resubscribe(TaskIdParams request, ClientCallContext context) throws A2AClientException {
118+
public void resubscribe(TaskIdParams request, @Nullable ClientCallContext context) throws A2AClientException {
116119
resubscribeToTask(request, null, null, context);
117120
}
118121

119122
@Override
120-
public void resubscribe(TaskIdParams request, List<BiConsumer<ClientEvent, AgentCard>> consumers,
121-
Consumer<Throwable> streamingErrorHandler, ClientCallContext context) throws A2AClientException {
123+
public void resubscribe(TaskIdParams request, @Nullable List<BiConsumer<ClientEvent, AgentCard>> consumers,
124+
@Nullable Consumer<Throwable> streamingErrorHandler, @Nullable ClientCallContext context) throws A2AClientException {
122125
resubscribeToTask(request, consumers, streamingErrorHandler, context);
123126
}
124127

125128
@Override
126-
public AgentCard getAgentCard(ClientCallContext context) throws A2AClientException {
129+
public AgentCard getAgentCard(@Nullable ClientCallContext context) throws A2AClientException {
127130
agentCard = clientTransport.getAgentCard(context);
128131
return agentCard;
129132
}
@@ -150,7 +153,7 @@ private ClientEvent getClientEvent(StreamingEventKind event, ClientTaskManager t
150153
}
151154
}
152155

153-
private MessageSendConfiguration createMessageSendConfiguration(PushNotificationConfig pushNotificationConfig) {
156+
private MessageSendConfiguration createMessageSendConfiguration(@Nullable PushNotificationConfig pushNotificationConfig) {
154157
return new MessageSendConfiguration.Builder()
155158
.acceptedOutputModes(clientConfig.getAcceptedOutputModes())
156159
.blocking(!clientConfig.isPolling())
@@ -159,8 +162,8 @@ private MessageSendConfiguration createMessageSendConfiguration(PushNotification
159162
.build();
160163
}
161164

162-
private void sendMessage(MessageSendParams messageSendParams, List<BiConsumer<ClientEvent, AgentCard>> consumers,
163-
Consumer<Throwable> errorHandler, ClientCallContext context) throws A2AClientException {
165+
private void sendMessage(MessageSendParams messageSendParams, @Nullable List<BiConsumer<ClientEvent, AgentCard>> consumers,
166+
@Nullable Consumer<Throwable> errorHandler, @Nullable ClientCallContext context) throws A2AClientException {
164167
if (! clientConfig.isStreaming() || ! agentCard.capabilities().streaming()) {
165168
EventKind eventKind = clientTransport.sendMessage(messageSendParams, context);
166169
ClientEvent clientEvent;
@@ -186,8 +189,8 @@ private void sendMessage(MessageSendParams messageSendParams, List<BiConsumer<Cl
186189
}
187190
}
188191

189-
private void resubscribeToTask(TaskIdParams request, List<BiConsumer<ClientEvent, AgentCard>> consumers,
190-
Consumer<Throwable> errorHandler, ClientCallContext context) throws A2AClientException {
192+
private void resubscribeToTask(TaskIdParams request, @Nullable List<BiConsumer<ClientEvent, AgentCard>> consumers,
193+
@Nullable Consumer<Throwable> errorHandler, @Nullable ClientCallContext context) throws A2AClientException {
191194
if (! clientConfig.isStreaming() || ! agentCard.capabilities().streaming()) {
192195
throw new A2AClientException("Client and/or server does not support resubscription");
193196
}
@@ -204,7 +207,7 @@ private void resubscribeToTask(TaskIdParams request, List<BiConsumer<ClientEvent
204207
clientTransport.resubscribe(request, eventHandler, overriddenErrorHandler, context);
205208
}
206209

207-
private Consumer<Throwable> getOverriddenErrorHandler(Consumer<Throwable> errorHandler) {
210+
private @NonNull Consumer<Throwable> getOverriddenErrorHandler(@Nullable Consumer<Throwable> errorHandler) {
208211
return e -> {
209212
if (errorHandler != null) {
210213
errorHandler.accept(e);
@@ -216,7 +219,7 @@ private Consumer<Throwable> getOverriddenErrorHandler(Consumer<Throwable> errorH
216219
};
217220
}
218221

219-
private void consume(ClientEvent clientEvent, AgentCard agentCard, List<BiConsumer<ClientEvent, AgentCard>> consumers) {
222+
private void consume(ClientEvent clientEvent, AgentCard agentCard, @Nullable List<BiConsumer<ClientEvent, AgentCard>> consumers) {
220223
if (consumers != null) {
221224
// use specified consumers
222225
for (BiConsumer<ClientEvent, AgentCard> consumer : consumers) {

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

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
import java.util.ServiceLoader;
1919
import java.util.function.BiConsumer;
2020
import java.util.function.Consumer;
21+
import org.jspecify.annotations.NonNull;
22+
import org.jspecify.annotations.Nullable;
2123

2224
public class ClientBuilder {
2325

@@ -35,12 +37,12 @@ public class ClientBuilder {
3537
private final AgentCard agentCard;
3638

3739
private final List<BiConsumer<ClientEvent, AgentCard>> consumers = new ArrayList<>();
38-
private Consumer<Throwable> streamErrorHandler;
39-
private ClientConfig clientConfig;
40+
private @Nullable Consumer<Throwable> streamErrorHandler;
41+
private ClientConfig clientConfig = new ClientConfig.Builder().build();
4042

4143
private final Map<Class<? extends ClientTransport>, ClientTransportConfig<? extends ClientTransport>> clientTransports = new LinkedHashMap<>();
4244

43-
ClientBuilder(AgentCard agentCard) {
45+
ClientBuilder(@NonNull AgentCard agentCard) {
4446
this.agentCard = agentCard;
4547
}
4648

@@ -69,7 +71,7 @@ public ClientBuilder streamingErrorHandler(Consumer<Throwable> streamErrorHandle
6971
return this;
7072
}
7173

72-
public ClientBuilder clientConfig(ClientConfig clientConfig) {
74+
public ClientBuilder clientConfig(@NonNull ClientConfig clientConfig) {
7375
this.clientConfig = clientConfig;
7476
return this;
7577
}
@@ -88,10 +90,13 @@ public Client build() throws A2AClientException {
8890
private ClientTransport buildClientTransport() throws A2AClientException {
8991
// Get the preferred transport
9092
AgentInterface agentInterface = findBestClientTransport();
91-
Class<? extends ClientTransport> transportProtocolClass = transportProviderRegistry.get(agentInterface.transport()).getTransportProtocolClass();
92-
93+
9394
// Get the transport provider associated to the protocol
9495
ClientTransportProvider clientTransportProvider = transportProviderRegistry.get(agentInterface.transport());
96+
if(clientTransportProvider == null) {
97+
throw new A2AClientException("Missing required TransportConfig for " + agentInterface.transport());
98+
}
99+
Class<? extends ClientTransport> transportProtocolClass = clientTransportProvider.getTransportProtocolClass();
95100

96101
// Retrieve the configuration associated to the preferred transport
97102
ClientTransportConfig<? extends ClientTransport> clientTransportConfig = clientTransports.get(transportProtocolClass);

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import io.a2a.spec.TaskState;
1717
import io.a2a.spec.TaskStatus;
1818
import io.a2a.spec.TaskStatusUpdateEvent;
19+
import org.jspecify.annotations.Nullable;
1920

2021
/**
2122
* Helps manage a task's lifecycle during the execution of a request.
@@ -24,9 +25,9 @@
2425
*/
2526
public class ClientTaskManager {
2627

27-
private Task currentTask;
28-
private String taskId;
29-
private String contextId;
28+
private @Nullable Task currentTask;
29+
private @Nullable String taskId;
30+
private @Nullable String contextId;
3031

3132
public ClientTaskManager() {
3233
this.currentTask = null;

0 commit comments

Comments
 (0)