Skip to content

Commit b9eee60

Browse files
jmesnilclaude
andcommitted
refactor!: Convert Message class to Java record
Refactored the Message class in the spec module from a final class to a Java record to reduce boilerplate code and leverage modern Java features. Key changes: - Converted Message to record with compact canonical constructor - Preserved validation and defensive copying logic - Added deprecated getter methods for backward compatibility - Updated Builder to use record accessor methods - Removed setTaskId() and setContextId() setters (use builder for immutability) Benefits: - Automatic equals(), hashCode(), and toString() implementations - Improved code readability and maintainability - Zero breaking changes for getter methods (deprecated methods maintain API compatibility) Breaking change note: This is marked as breaking because: 1. The underlying class structure changed from a class to a record 2. The setTaskId() and setContextId() methods were removed (records are immutable) See ADR 0002 (doc/adr/0002-refactor-domain-classes-to-java-records.md) for detailed architectural decision and rationale. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1 parent 30fd3fd commit b9eee60

File tree

41 files changed

+362
-344
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+362
-344
lines changed

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ private static Message toMessage(String text, Message.Role role, String messageI
106106
}
107107

108108
private static Message toMessage(String text, Message.Role role, String messageId, String contextId, String taskId) {
109-
Message.Builder messageBuilder = new Message.Builder()
109+
Message.Builder messageBuilder = Message.builder()
110110
.role(role)
111111
.parts(Collections.singletonList(new TextPart(text)))
112112
.contextId(contextId)
@@ -118,7 +118,7 @@ private static Message toMessage(String text, Message.Role role, String messageI
118118
}
119119

120120
private static Message toMessage(List<Part<?>> parts, Message.Role role, String messageId, String contextId, String taskId) {
121-
Message.Builder messageBuilder = new Message.Builder()
121+
Message.Builder messageBuilder = Message.builder()
122122
.role(role)
123123
.parts(parts)
124124
.contextId(contextId)

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

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,16 +24,6 @@ public Message getMessage() {
2424

2525
@Override
2626
public String toString() {
27-
String messageAsString = "{"
28-
+ "role=" + message.getRole()
29-
+ ", parts=" + message.getParts()
30-
+ ", messageId=" + message.getMessageId()
31-
+ ", contextId=" + message.getContextId()
32-
+ ", taskId=" + message.getTaskId()
33-
+ ", metadata=" + message.getMetadata()
34-
+ ", kind=" + message.getKind()
35-
+ ", referenceTaskIds=" + message.getReferenceTaskIds()
36-
+ ", extensions=" + message.getExtensions() + '}';
37-
return "MessageEvent{" + "message=" + messageAsString + '}';
27+
return "MessageEvent{" + "message=" + message + '}';
3828
}
3929
}

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

Lines changed: 39 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,12 @@ public void testToUserMessage() {
2020
String text = "Hello, world!";
2121
Message message = A2A.toUserMessage(text);
2222

23-
assertEquals(Message.Role.USER, message.getRole());
24-
assertEquals(1, message.getParts().size());
25-
assertEquals(text, ((TextPart) message.getParts().get(0)).text());
26-
assertNotNull(message.getMessageId());
27-
assertNull(message.getContextId());
28-
assertNull(message.getTaskId());
23+
assertEquals(Message.Role.USER, message.role());
24+
assertEquals(1, message.parts().size());
25+
assertEquals(text, ((TextPart) message.parts().get(0)).text());
26+
assertNotNull(message.messageId());
27+
assertNull(message.contextId());
28+
assertNull(message.taskId());
2929
}
3030

3131
@Test
@@ -34,19 +34,19 @@ public void testToUserMessageWithId() {
3434
String messageId = "test-message-id";
3535
Message message = A2A.toUserMessage(text, messageId);
3636

37-
assertEquals(Message.Role.USER, message.getRole());
38-
assertEquals(messageId, message.getMessageId());
37+
assertEquals(Message.Role.USER, message.role());
38+
assertEquals(messageId, message.messageId());
3939
}
4040

4141
@Test
4242
public void testToAgentMessage() {
4343
String text = "Hello, I'm an agent!";
4444
Message message = A2A.toAgentMessage(text);
4545

46-
assertEquals(Message.Role.AGENT, message.getRole());
47-
assertEquals(1, message.getParts().size());
48-
assertEquals(text, ((TextPart) message.getParts().get(0)).text());
49-
assertNotNull(message.getMessageId());
46+
assertEquals(Message.Role.AGENT, message.role());
47+
assertEquals(1, message.parts().size());
48+
assertEquals(text, ((TextPart) message.parts().get(0)).text());
49+
assertNotNull(message.messageId());
5050
}
5151

5252
@Test
@@ -55,8 +55,8 @@ public void testToAgentMessageWithId() {
5555
String messageId = "agent-message-id";
5656
Message message = A2A.toAgentMessage(text, messageId);
5757

58-
assertEquals(Message.Role.AGENT, message.getRole());
59-
assertEquals(messageId, message.getMessageId());
58+
assertEquals(Message.Role.AGENT, message.role());
59+
assertEquals(messageId, message.messageId());
6060
}
6161

6262
@Test
@@ -67,14 +67,14 @@ public void testCreateUserTextMessage() {
6767

6868
Message message = A2A.createUserTextMessage(text, contextId, taskId);
6969

70-
assertEquals(Message.Role.USER, message.getRole());
71-
assertEquals(contextId, message.getContextId());
72-
assertEquals(taskId, message.getTaskId());
73-
assertEquals(1, message.getParts().size());
74-
assertEquals(text, ((TextPart) message.getParts().get(0)).text());
75-
assertNotNull(message.getMessageId());
76-
assertNull(message.getMetadata());
77-
assertNull(message.getReferenceTaskIds());
70+
assertEquals(Message.Role.USER, message.role());
71+
assertEquals(contextId, message.contextId());
72+
assertEquals(taskId, message.taskId());
73+
assertEquals(1, message.parts().size());
74+
assertEquals(text, ((TextPart) message.parts().get(0)).text());
75+
assertNotNull(message.messageId());
76+
assertNull(message.metadata());
77+
assertNull(message.referenceTaskIds());
7878
}
7979

8080
@Test
@@ -83,11 +83,11 @@ public void testCreateUserTextMessageWithNullParams() {
8383

8484
Message message = A2A.createUserTextMessage(text, null, null);
8585

86-
assertEquals(Message.Role.USER, message.getRole());
87-
assertNull(message.getContextId());
88-
assertNull(message.getTaskId());
89-
assertEquals(1, message.getParts().size());
90-
assertEquals(text, ((TextPart) message.getParts().get(0)).text());
86+
assertEquals(Message.Role.USER, message.role());
87+
assertNull(message.contextId());
88+
assertNull(message.taskId());
89+
assertEquals(1, message.parts().size());
90+
assertEquals(text, ((TextPart) message.parts().get(0)).text());
9191
}
9292

9393
@Test
@@ -98,12 +98,12 @@ public void testCreateAgentTextMessage() {
9898

9999
Message message = A2A.createAgentTextMessage(text, contextId, taskId);
100100

101-
assertEquals(Message.Role.AGENT, message.getRole());
102-
assertEquals(contextId, message.getContextId());
103-
assertEquals(taskId, message.getTaskId());
104-
assertEquals(1, message.getParts().size());
105-
assertEquals(text, ((TextPart) message.getParts().get(0)).text());
106-
assertNotNull(message.getMessageId());
101+
assertEquals(Message.Role.AGENT, message.role());
102+
assertEquals(contextId, message.contextId());
103+
assertEquals(taskId, message.taskId());
104+
assertEquals(1, message.parts().size());
105+
assertEquals(text, ((TextPart) message.parts().get(0)).text());
106+
assertNotNull(message.messageId());
107107
}
108108

109109
@Test
@@ -117,12 +117,12 @@ public void testCreateAgentPartsMessage() {
117117

118118
Message message = A2A.createAgentPartsMessage(parts, contextId, taskId);
119119

120-
assertEquals(Message.Role.AGENT, message.getRole());
121-
assertEquals(contextId, message.getContextId());
122-
assertEquals(taskId, message.getTaskId());
123-
assertEquals(2, message.getParts().size());
124-
assertEquals("Part 1", ((TextPart) message.getParts().get(0)).text());
125-
assertEquals("Part 2", ((TextPart) message.getParts().get(1)).text());
120+
assertEquals(Message.Role.AGENT, message.role());
121+
assertEquals(contextId, message.contextId());
122+
assertEquals(taskId, message.taskId());
123+
assertEquals(2, message.parts().size());
124+
assertEquals("Part 1", ((TextPart) message.parts().get(0)).text());
125+
assertEquals("Part 2", ((TextPart) message.parts().get(1)).text());
126126
}
127127

128128
@Test

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ public class AuthenticationAuthorizationTest {
6565
@BeforeEach
6666
public void setUp() {
6767
server = new ClientAndServer(4001);
68-
MESSAGE = new Message.Builder()
68+
MESSAGE = Message.builder()
6969
.role(Message.Role.USER)
7070
.parts(Collections.singletonList(new TextPart("test message")))
7171
.contextId("context-1234")

client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportStreamingTest.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ public void tearDown() {
5454
public void testSendStreamingMessageParams() {
5555
// The goal here is just to verify the correct parameters are being used
5656
// This is a unit test of the parameter construction, not the streaming itself
57-
Message message = new Message.Builder()
57+
Message message = Message.builder()
5858
.role(Message.Role.USER)
5959
.parts(Collections.singletonList(new TextPart("test message")))
6060
.contextId("context-test")
@@ -74,8 +74,8 @@ public void testSendStreamingMessageParams() {
7474
assertNotNull(params);
7575
assertEquals(message, params.message());
7676
assertEquals(configuration, params.configuration());
77-
assertEquals(Message.Role.USER, params.message().getRole());
78-
assertEquals("test message", ((TextPart) params.message().getParts().get(0)).text());
77+
assertEquals(Message.Role.USER, params.message().role());
78+
assertEquals("test message", ((TextPart) params.message().parts().get(0)).text());
7979
}
8080

8181
@Test
@@ -95,7 +95,7 @@ public void testA2AClientSendStreamingMessage() throws Exception {
9595
);
9696

9797
JSONRPCTransport client = new JSONRPCTransport("http://localhost:4001");
98-
Message message = new Message.Builder()
98+
Message message = Message.builder()
9999
.role(Message.Role.USER)
100100
.parts(Collections.singletonList(new TextPart("tell me some jokes")))
101101
.contextId("context-1234")

client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JSONRPCTransportTest.java

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ public void testA2AClientSendMessage() throws Exception {
104104
);
105105

106106
JSONRPCTransport client = new JSONRPCTransport("http://localhost:4001");
107-
Message message = new Message.Builder()
107+
Message message = Message.builder()
108108
.role(Message.Role.USER)
109109
.parts(Collections.singletonList(new TextPart("tell me a joke")))
110110
.contextId("context-1234")
@@ -152,7 +152,7 @@ public void testA2AClientSendMessageWithMessageResponse() throws Exception {
152152
);
153153

154154
JSONRPCTransport client = new JSONRPCTransport("http://localhost:4001");
155-
Message message = new Message.Builder()
155+
Message message = Message.builder()
156156
.role(Message.Role.USER)
157157
.parts(Collections.singletonList(new TextPart("tell me a joke")))
158158
.contextId("context-1234")
@@ -170,11 +170,11 @@ public void testA2AClientSendMessageWithMessageResponse() throws Exception {
170170
EventKind result = client.sendMessage(params, null);
171171
assertInstanceOf(Message.class, result);
172172
Message agentMessage = (Message) result;
173-
assertEquals(Message.Role.AGENT, agentMessage.getRole());
174-
Part<?> part = agentMessage.getParts().get(0);
173+
assertEquals(Message.Role.AGENT, agentMessage.role());
174+
Part<?> part = agentMessage.parts().get(0);
175175
assertEquals(Part.Kind.TEXT, part.kind());
176176
assertEquals("Why did the chicken cross the road? To get to the other side!", ((TextPart) part).text());
177-
assertEquals("msg-456", agentMessage.getMessageId());
177+
assertEquals("msg-456", agentMessage.messageId());
178178
}
179179

180180

@@ -194,7 +194,7 @@ public void testA2AClientSendMessageWithError() throws Exception {
194194
);
195195

196196
JSONRPCTransport client = new JSONRPCTransport("http://localhost:4001");
197-
Message message = new Message.Builder()
197+
Message message = Message.builder()
198198
.role(Message.Role.USER)
199199
.parts(Collections.singletonList(new TextPart("tell me a joke")))
200200
.contextId("context-1234")
@@ -250,8 +250,8 @@ public void testA2AClientGetTask() throws Exception {
250250
assertNotNull(history);
251251
assertEquals(1, history.size());
252252
Message message = history.get(0);
253-
assertEquals(Message.Role.USER, message.getRole());
254-
List<Part<?>> parts = message.getParts();
253+
assertEquals(Message.Role.USER, message.role());
254+
List<Part<?>> parts = message.parts();
255255
assertNotNull(parts);
256256
assertEquals(3, parts.size());
257257
part = parts.get(0);
@@ -532,7 +532,7 @@ public void testA2AClientSendMessageWithFilePart() throws Exception {
532532
);
533533

534534
JSONRPCTransport client = new JSONRPCTransport("http://localhost:4001");
535-
Message message = new Message.Builder()
535+
Message message = Message.builder()
536536
.role(Message.Role.USER)
537537
.parts(List.of(
538538
new TextPart("analyze this image"),
@@ -592,7 +592,7 @@ public void testA2AClientSendMessageWithDataPart() throws Exception {
592592
data.put("location", "San Francisco");
593593
data.put("timestamp", "2024-01-15T10:30:00Z");
594594

595-
Message message = new Message.Builder()
595+
Message message = Message.builder()
596596
.role(Message.Role.USER)
597597
.parts(List.of(
598598
new TextPart("process this data"),
@@ -649,7 +649,7 @@ public void testA2AClientSendMessageWithMixedParts() throws Exception {
649649
data.put("dataPoints", List.of(10, 20, 30, 40));
650650
data.put("labels", List.of("Q1", "Q2", "Q3", "Q4"));
651651

652-
Message message = new Message.Builder()
652+
Message message = Message.builder()
653653
.role(Message.Role.USER)
654654
.parts(List.of(
655655
new TextPart("analyze this data and image"),

client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/sse/SSEEventListenerTest.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -74,12 +74,12 @@ public void testOnEventWithMessageResult() throws Exception {
7474
assertNotNull(receivedEvent.get());
7575
assertTrue(receivedEvent.get() instanceof Message);
7676
Message message = (Message) receivedEvent.get();
77-
assertEquals(Message.Role.AGENT, message.getRole());
78-
assertEquals("msg-123", message.getMessageId());
79-
assertEquals("context-456", message.getContextId());
80-
assertEquals(1, message.getParts().size());
81-
assertTrue(message.getParts().get(0) instanceof TextPart);
82-
assertEquals("Hello, world!", ((TextPart) message.getParts().get(0)).text());
77+
assertEquals(Message.Role.AGENT, message.role());
78+
assertEquals("msg-123", message.messageId());
79+
assertEquals("context-456", message.contextId());
80+
assertEquals(1, message.parts().size());
81+
assertTrue(message.parts().get(0) instanceof TextPart);
82+
assertEquals("Hello, world!", ((TextPart) message.parts().get(0)).text());
8383
}
8484

8585
@Test

client/transport/rest/src/test/java/io/a2a/client/transport/rest/RestTransportTest.java

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ public RestTransportTest() {
108108
*/
109109
@Test
110110
public void testSendMessage() throws Exception {
111-
Message message = new Message.Builder()
111+
Message message = Message.builder()
112112
.role(Message.Role.USER)
113113
.parts(Collections.singletonList(new TextPart("tell me a joke")))
114114
.contextId("context-1234")
@@ -142,15 +142,15 @@ public void testSendMessage() throws Exception {
142142
assertEquals(1, task.history().size());
143143
Message history = task.history().get(0);
144144
assertEquals("message", history.getKind());
145-
assertEquals(Message.Role.USER, history.getRole());
146-
assertEquals("context-1234", history.getContextId());
147-
assertEquals("message-1234", history.getMessageId());
148-
assertEquals("9b511af4-b27c-47fa-aecf-2a93c08a44f8", history.getTaskId());
149-
assertEquals(1, history.getParts().size());
150-
assertEquals(Kind.TEXT, history.getParts().get(0).kind());
151-
assertEquals("tell me a joke", ((TextPart) history.getParts().get(0)).text());
145+
assertEquals(Message.Role.USER, history.role());
146+
assertEquals("context-1234", history.contextId());
147+
assertEquals("message-1234", history.messageId());
148+
assertEquals("9b511af4-b27c-47fa-aecf-2a93c08a44f8", history.taskId());
149+
assertEquals(1, history.parts().size());
150+
assertEquals(Kind.TEXT, history.parts().get(0).kind());
151+
assertEquals("tell me a joke", ((TextPart) history.parts().get(0)).text());
152152
assertNull(task.metadata());
153-
assertNull(history.getReferenceTaskIds());
153+
assertNull(history.referenceTaskIds());
154154
}
155155

156156
/**
@@ -215,21 +215,21 @@ public void testGetTask() throws Exception {
215215
assertEquals(1, task.history().size());
216216
Message history = task.history().get(0);
217217
assertEquals("message", history.getKind());
218-
assertEquals(Message.Role.USER, history.getRole());
219-
assertEquals("message-123", history.getMessageId());
220-
assertEquals(3, history.getParts().size());
221-
assertEquals(Kind.TEXT, history.getParts().get(0).kind());
222-
assertEquals("tell me a joke", ((TextPart) history.getParts().get(0)).text());
223-
assertEquals(Kind.FILE, history.getParts().get(1).kind());
224-
FilePart part = (FilePart) history.getParts().get(1);
218+
assertEquals(Message.Role.USER, history.role());
219+
assertEquals("message-123", history.messageId());
220+
assertEquals(3, history.parts().size());
221+
assertEquals(Kind.TEXT, history.parts().get(0).kind());
222+
assertEquals("tell me a joke", ((TextPart) history.parts().get(0)).text());
223+
assertEquals(Kind.FILE, history.parts().get(1).kind());
224+
FilePart part = (FilePart) history.parts().get(1);
225225
assertEquals("text/plain", part.file().mimeType());
226226
assertEquals("file:///path/to/file.txt", ((FileWithUri) part.file()).uri());
227-
part = (FilePart) history.getParts().get(2);
227+
part = (FilePart) history.parts().get(2);
228228
assertEquals(Kind.FILE, part.kind());
229229
assertEquals("text/plain", part.file().mimeType());
230230
assertEquals("aGVsbG8=", ((FileWithBytes) part.file()).bytes());
231-
assertNull(history.getMetadata());
232-
assertNull(history.getReferenceTaskIds());
231+
assertNull(history.metadata());
232+
assertNull(history.referenceTaskIds());
233233
}
234234

235235
/**
@@ -251,7 +251,7 @@ public void testSendMessageStreaming() throws Exception {
251251
);
252252

253253
RestTransport client = new RestTransport(CARD);
254-
Message message = new Message.Builder()
254+
Message message = Message.builder()
255255
.role(Message.Role.USER)
256256
.parts(Collections.singletonList(new TextPart("tell me some jokes")))
257257
.contextId("context-1234")

0 commit comments

Comments
 (0)