Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
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 <noreply@anthropic.com>
  • Loading branch information
jmesnil and claude committed Dec 5, 2025
commit 9a37dc37bca8a96c95569dea879ffe63c4eb3f5d
4 changes: 2 additions & 2 deletions client/base/src/main/java/io/a2a/A2A.java
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ private static Message toMessage(String text, Message.Role role, String messageI
}

private static Message toMessage(String text, Message.Role role, String messageId, String contextId, String taskId) {
Message.Builder messageBuilder = new Message.Builder()
Message.Builder messageBuilder = Message.builder()
.role(role)
.parts(Collections.singletonList(new TextPart(text)))
.contextId(contextId)
Expand All @@ -118,7 +118,7 @@ private static Message toMessage(String text, Message.Role role, String messageI
}

private static Message toMessage(List<Part<?>> parts, Message.Role role, String messageId, String contextId, String taskId) {
Message.Builder messageBuilder = new Message.Builder()
Message.Builder messageBuilder = Message.builder()
.role(role)
.parts(parts)
.contextId(contextId)
Expand Down
12 changes: 1 addition & 11 deletions client/base/src/main/java/io/a2a/client/MessageEvent.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,6 @@ public Message getMessage() {

@Override
public String toString() {
String messageAsString = "{"
+ "role=" + message.getRole()
+ ", parts=" + message.getParts()
+ ", messageId=" + message.getMessageId()
+ ", contextId=" + message.getContextId()
+ ", taskId=" + message.getTaskId()
+ ", metadata=" + message.getMetadata()
+ ", kind=" + message.getKind()
+ ", referenceTaskIds=" + message.getReferenceTaskIds()
+ ", extensions=" + message.getExtensions() + '}';
return "MessageEvent{" + "message=" + messageAsString + '}';
return "MessageEvent{" + "message=" + message + '}';
}
}
78 changes: 39 additions & 39 deletions client/base/src/test/java/io/a2a/A2ATest.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@ public void testToUserMessage() {
String text = "Hello, world!";
Message message = A2A.toUserMessage(text);

assertEquals(Message.Role.USER, message.getRole());
assertEquals(1, message.getParts().size());
assertEquals(text, ((TextPart) message.getParts().get(0)).text());
assertNotNull(message.getMessageId());
assertNull(message.getContextId());
assertNull(message.getTaskId());
assertEquals(Message.Role.USER, message.role());
assertEquals(1, message.parts().size());
assertEquals(text, ((TextPart) message.parts().get(0)).text());
assertNotNull(message.messageId());
assertNull(message.contextId());
assertNull(message.taskId());
}

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

assertEquals(Message.Role.USER, message.getRole());
assertEquals(messageId, message.getMessageId());
assertEquals(Message.Role.USER, message.role());
assertEquals(messageId, message.messageId());
}

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

assertEquals(Message.Role.AGENT, message.getRole());
assertEquals(1, message.getParts().size());
assertEquals(text, ((TextPart) message.getParts().get(0)).text());
assertNotNull(message.getMessageId());
assertEquals(Message.Role.AGENT, message.role());
assertEquals(1, message.parts().size());
assertEquals(text, ((TextPart) message.parts().get(0)).text());
assertNotNull(message.messageId());
}

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

assertEquals(Message.Role.AGENT, message.getRole());
assertEquals(messageId, message.getMessageId());
assertEquals(Message.Role.AGENT, message.role());
assertEquals(messageId, message.messageId());
}

@Test
Expand All @@ -67,14 +67,14 @@ public void testCreateUserTextMessage() {

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

assertEquals(Message.Role.USER, message.getRole());
assertEquals(contextId, message.getContextId());
assertEquals(taskId, message.getTaskId());
assertEquals(1, message.getParts().size());
assertEquals(text, ((TextPart) message.getParts().get(0)).text());
assertNotNull(message.getMessageId());
assertNull(message.getMetadata());
assertNull(message.getReferenceTaskIds());
assertEquals(Message.Role.USER, message.role());
assertEquals(contextId, message.contextId());
assertEquals(taskId, message.taskId());
assertEquals(1, message.parts().size());
assertEquals(text, ((TextPart) message.parts().get(0)).text());
assertNotNull(message.messageId());
assertNull(message.metadata());
assertNull(message.referenceTaskIds());
}

@Test
Expand All @@ -83,11 +83,11 @@ public void testCreateUserTextMessageWithNullParams() {

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

assertEquals(Message.Role.USER, message.getRole());
assertNull(message.getContextId());
assertNull(message.getTaskId());
assertEquals(1, message.getParts().size());
assertEquals(text, ((TextPart) message.getParts().get(0)).text());
assertEquals(Message.Role.USER, message.role());
assertNull(message.contextId());
assertNull(message.taskId());
assertEquals(1, message.parts().size());
assertEquals(text, ((TextPart) message.parts().get(0)).text());
}

@Test
Expand All @@ -98,12 +98,12 @@ public void testCreateAgentTextMessage() {

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

assertEquals(Message.Role.AGENT, message.getRole());
assertEquals(contextId, message.getContextId());
assertEquals(taskId, message.getTaskId());
assertEquals(1, message.getParts().size());
assertEquals(text, ((TextPart) message.getParts().get(0)).text());
assertNotNull(message.getMessageId());
assertEquals(Message.Role.AGENT, message.role());
assertEquals(contextId, message.contextId());
assertEquals(taskId, message.taskId());
assertEquals(1, message.parts().size());
assertEquals(text, ((TextPart) message.parts().get(0)).text());
assertNotNull(message.messageId());
}

@Test
Expand All @@ -117,12 +117,12 @@ public void testCreateAgentPartsMessage() {

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

assertEquals(Message.Role.AGENT, message.getRole());
assertEquals(contextId, message.getContextId());
assertEquals(taskId, message.getTaskId());
assertEquals(2, message.getParts().size());
assertEquals("Part 1", ((TextPart) message.getParts().get(0)).text());
assertEquals("Part 2", ((TextPart) message.getParts().get(1)).text());
assertEquals(Message.Role.AGENT, message.role());
assertEquals(contextId, message.contextId());
assertEquals(taskId, message.taskId());
assertEquals(2, message.parts().size());
assertEquals("Part 1", ((TextPart) message.parts().get(0)).text());
assertEquals("Part 2", ((TextPart) message.parts().get(1)).text());
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public class AuthenticationAuthorizationTest {
@BeforeEach
public void setUp() {
server = new ClientAndServer(4001);
MESSAGE = new Message.Builder()
MESSAGE = Message.builder()
.role(Message.Role.USER)
.parts(Collections.singletonList(new TextPart("test message")))
.contextId("context-1234")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public void tearDown() {
public void testSendStreamingMessageParams() {
// The goal here is just to verify the correct parameters are being used
// This is a unit test of the parameter construction, not the streaming itself
Message message = new Message.Builder()
Message message = Message.builder()
.role(Message.Role.USER)
.parts(Collections.singletonList(new TextPart("test message")))
.contextId("context-test")
Expand All @@ -74,8 +74,8 @@ public void testSendStreamingMessageParams() {
assertNotNull(params);
assertEquals(message, params.message());
assertEquals(configuration, params.configuration());
assertEquals(Message.Role.USER, params.message().getRole());
assertEquals("test message", ((TextPart) params.message().getParts().get(0)).text());
assertEquals(Message.Role.USER, params.message().role());
assertEquals("test message", ((TextPart) params.message().parts().get(0)).text());
}

@Test
Expand All @@ -95,7 +95,7 @@ public void testA2AClientSendStreamingMessage() throws Exception {
);

JSONRPCTransport client = new JSONRPCTransport("http://localhost:4001");
Message message = new Message.Builder()
Message message = Message.builder()
.role(Message.Role.USER)
.parts(Collections.singletonList(new TextPart("tell me some jokes")))
.contextId("context-1234")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ public void testA2AClientSendMessage() throws Exception {
);

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

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


Expand All @@ -194,7 +194,7 @@ public void testA2AClientSendMessageWithError() throws Exception {
);

JSONRPCTransport client = new JSONRPCTransport("http://localhost:4001");
Message message = new Message.Builder()
Message message = Message.builder()
.role(Message.Role.USER)
.parts(Collections.singletonList(new TextPart("tell me a joke")))
.contextId("context-1234")
Expand Down Expand Up @@ -250,8 +250,8 @@ public void testA2AClientGetTask() throws Exception {
assertNotNull(history);
assertEquals(1, history.size());
Message message = history.get(0);
assertEquals(Message.Role.USER, message.getRole());
List<Part<?>> parts = message.getParts();
assertEquals(Message.Role.USER, message.role());
List<Part<?>> parts = message.parts();
assertNotNull(parts);
assertEquals(3, parts.size());
part = parts.get(0);
Expand Down Expand Up @@ -532,7 +532,7 @@ public void testA2AClientSendMessageWithFilePart() throws Exception {
);

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

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

Message message = new Message.Builder()
Message message = Message.builder()
.role(Message.Role.USER)
.parts(List.of(
new TextPart("analyze this data and image"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,12 +74,12 @@ public void testOnEventWithMessageResult() throws Exception {
assertNotNull(receivedEvent.get());
assertTrue(receivedEvent.get() instanceof Message);
Message message = (Message) receivedEvent.get();
assertEquals(Message.Role.AGENT, message.getRole());
assertEquals("msg-123", message.getMessageId());
assertEquals("context-456", message.getContextId());
assertEquals(1, message.getParts().size());
assertTrue(message.getParts().get(0) instanceof TextPart);
assertEquals("Hello, world!", ((TextPart) message.getParts().get(0)).text());
assertEquals(Message.Role.AGENT, message.role());
assertEquals("msg-123", message.messageId());
assertEquals("context-456", message.contextId());
assertEquals(1, message.parts().size());
assertTrue(message.parts().get(0) instanceof TextPart);
assertEquals("Hello, world!", ((TextPart) message.parts().get(0)).text());
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ public RestTransportTest() {
*/
@Test
public void testSendMessage() throws Exception {
Message message = new Message.Builder()
Message message = Message.builder()
.role(Message.Role.USER)
.parts(Collections.singletonList(new TextPart("tell me a joke")))
.contextId("context-1234")
Expand Down Expand Up @@ -142,15 +142,15 @@ public void testSendMessage() throws Exception {
assertEquals(1, task.history().size());
Message history = task.history().get(0);
assertEquals("message", history.getKind());
assertEquals(Message.Role.USER, history.getRole());
assertEquals("context-1234", history.getContextId());
assertEquals("message-1234", history.getMessageId());
assertEquals("9b511af4-b27c-47fa-aecf-2a93c08a44f8", history.getTaskId());
assertEquals(1, history.getParts().size());
assertEquals(Kind.TEXT, history.getParts().get(0).kind());
assertEquals("tell me a joke", ((TextPart) history.getParts().get(0)).text());
assertEquals(Message.Role.USER, history.role());
assertEquals("context-1234", history.contextId());
assertEquals("message-1234", history.messageId());
assertEquals("9b511af4-b27c-47fa-aecf-2a93c08a44f8", history.taskId());
assertEquals(1, history.parts().size());
assertEquals(Kind.TEXT, history.parts().get(0).kind());
assertEquals("tell me a joke", ((TextPart) history.parts().get(0)).text());
assertNull(task.metadata());
assertNull(history.getReferenceTaskIds());
assertNull(history.referenceTaskIds());
}

/**
Expand Down Expand Up @@ -215,21 +215,21 @@ public void testGetTask() throws Exception {
assertEquals(1, task.history().size());
Message history = task.history().get(0);
assertEquals("message", history.getKind());
assertEquals(Message.Role.USER, history.getRole());
assertEquals("message-123", history.getMessageId());
assertEquals(3, history.getParts().size());
assertEquals(Kind.TEXT, history.getParts().get(0).kind());
assertEquals("tell me a joke", ((TextPart) history.getParts().get(0)).text());
assertEquals(Kind.FILE, history.getParts().get(1).kind());
FilePart part = (FilePart) history.getParts().get(1);
assertEquals(Message.Role.USER, history.role());
assertEquals("message-123", history.messageId());
assertEquals(3, history.parts().size());
assertEquals(Kind.TEXT, history.parts().get(0).kind());
assertEquals("tell me a joke", ((TextPart) history.parts().get(0)).text());
assertEquals(Kind.FILE, history.parts().get(1).kind());
FilePart part = (FilePart) history.parts().get(1);
assertEquals("text/plain", part.file().mimeType());
assertEquals("file:///path/to/file.txt", ((FileWithUri) part.file()).uri());
part = (FilePart) history.getParts().get(2);
part = (FilePart) history.parts().get(2);
assertEquals(Kind.FILE, part.kind());
assertEquals("text/plain", part.file().mimeType());
assertEquals("aGVsbG8=", ((FileWithBytes) part.file()).bytes());
assertNull(history.getMetadata());
assertNull(history.getReferenceTaskIds());
assertNull(history.metadata());
assertNull(history.referenceTaskIds());
}

/**
Expand All @@ -251,7 +251,7 @@ public void testSendMessageStreaming() throws Exception {
);

RestTransport client = new RestTransport(CARD);
Message message = new Message.Builder()
Message message = Message.builder()
.role(Message.Role.USER)
.parts(Collections.singletonList(new TextPart("tell me some jokes")))
.contextId("context-1234")
Expand Down
Loading