Skip to content

Commit 8925a77

Browse files
committed
Add the transport/* modules
Comment out usage of DefaultRequestHandler until we have the payload conversion. Disable tests since the code is not runnable yet
1 parent c0d7d5e commit 8925a77

18 files changed

Lines changed: 411 additions & 2 deletions

File tree

compat-0.3/pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,8 +128,8 @@
128128
<module>reference/grpc</module>
129129
<module>reference/rest</module>
130130

131-
<!-- TCK -->
132-
<module>tck</module>
131+
<!-- TCK - temporarily disabled until transport and reference modules are ported -->
132+
<!-- <module>tck</module> -->
133133
</modules>
134134

135135
</project>
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package io.a2a.tck.server;
2+
3+
import java.util.Collections;
4+
import java.util.List;
5+
6+
import jakarta.enterprise.context.ApplicationScoped;
7+
import jakarta.enterprise.inject.Produces;
8+
9+
import io.a2a.server.PublicAgentCard;
10+
import io.a2a.spec.AgentCapabilities;
11+
import io.a2a.spec.AgentCard;
12+
import io.a2a.spec.AgentInterface;
13+
import io.a2a.spec.AgentSkill;
14+
import io.a2a.spec.TransportProtocol;
15+
16+
@ApplicationScoped
17+
public class AgentCardProducer {
18+
19+
private static final String DEFAULT_SUT_URL = "http://localhost:9999";
20+
21+
@Produces
22+
@PublicAgentCard
23+
public AgentCard agentCard() {
24+
25+
String sutJsonRpcUrl = getEnvOrDefault("SUT_JSONRPC_URL", DEFAULT_SUT_URL);
26+
String sutGrpcUrl = getEnvOrDefault("SUT_GRPC_URL", DEFAULT_SUT_URL);
27+
String sutRestcUrl = getEnvOrDefault("SUT_REST_URL", DEFAULT_SUT_URL);
28+
return new AgentCard.Builder()
29+
.name("Hello World Agent")
30+
.description("Just a hello world agent")
31+
.url(sutJsonRpcUrl)
32+
.version("1.0.0")
33+
.documentationUrl("http://example.com/docs")
34+
.capabilities(new AgentCapabilities.Builder()
35+
.streaming(true)
36+
.pushNotifications(true)
37+
.stateTransitionHistory(true)
38+
.build())
39+
.defaultInputModes(Collections.singletonList("text"))
40+
.defaultOutputModes(Collections.singletonList("text"))
41+
.skills(Collections.singletonList(new AgentSkill.Builder()
42+
.id("hello_world")
43+
.name("Returns hello world")
44+
.description("just returns hello world")
45+
.tags(Collections.singletonList("hello world"))
46+
.examples(List.of("hi", "hello world"))
47+
.build()))
48+
.protocolVersion("0.3.0")
49+
.additionalInterfaces(List.of(
50+
new AgentInterface(TransportProtocol.JSONRPC.asString(), sutJsonRpcUrl),
51+
new AgentInterface(TransportProtocol.GRPC.asString(), sutGrpcUrl),
52+
new AgentInterface(TransportProtocol.HTTP_JSON.asString(), sutRestcUrl)))
53+
.build();
54+
}
55+
56+
private static String getEnvOrDefault(String envVar, String defaultValue) {
57+
String value = System.getenv(envVar);
58+
return value == null || value.isBlank() ? defaultValue : value;
59+
}
60+
}
61+
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
package io.a2a.tck.server;
2+
3+
import jakarta.annotation.PreDestroy;
4+
import jakarta.enterprise.context.ApplicationScoped;
5+
import jakarta.enterprise.inject.Produces;
6+
7+
import io.a2a.server.agentexecution.AgentExecutor;
8+
import io.a2a.server.agentexecution.RequestContext;
9+
import io.a2a.server.events.EventQueue;
10+
import io.a2a.server.tasks.TaskUpdater;
11+
import io.a2a.spec.JSONRPCError;
12+
import io.a2a.spec.Task;
13+
import io.a2a.spec.TaskNotCancelableError;
14+
import io.a2a.spec.TaskState;
15+
import io.a2a.spec.TaskStatus;
16+
import io.a2a.spec.TaskStatusUpdateEvent;
17+
18+
@ApplicationScoped
19+
public class AgentExecutorProducer {
20+
21+
@Produces
22+
public AgentExecutor agentExecutor() {
23+
return new FireAndForgetAgentExecutor();
24+
}
25+
26+
private static class FireAndForgetAgentExecutor implements AgentExecutor {
27+
@Override
28+
public void execute(RequestContext context, EventQueue eventQueue) throws JSONRPCError {
29+
Task task = context.getTask();
30+
31+
if (task == null) {
32+
task = new Task.Builder()
33+
.id(context.getTaskId())
34+
.contextId(context.getContextId())
35+
.status(new TaskStatus(TaskState.SUBMITTED))
36+
.history(context.getMessage())
37+
.build();
38+
eventQueue.enqueueEvent(task);
39+
}
40+
41+
// Sleep to allow task state persistence before TCK resubscribe test
42+
if (context.getMessage().getMessageId().startsWith("test-resubscribe-message-id")) {
43+
int timeoutMs = Integer.parseInt(System.getenv().getOrDefault("RESUBSCRIBE_TIMEOUT_MS", "3000"));
44+
System.out.println("====> task id starts with test-resubscribe-message-id, sleeping for " + timeoutMs + " ms");
45+
try {
46+
Thread.sleep(timeoutMs);
47+
} catch (InterruptedException e) {
48+
Thread.currentThread().interrupt();
49+
}
50+
}
51+
TaskUpdater updater = new TaskUpdater(context, eventQueue);
52+
53+
// Immediately set to WORKING state
54+
updater.startWork();
55+
System.out.println("====> task set to WORKING, starting background execution");
56+
57+
// Method returns immediately - task continues in background
58+
System.out.println("====> execute() method returning immediately, task running in background");
59+
}
60+
61+
@Override
62+
public void cancel(RequestContext context, EventQueue eventQueue) throws JSONRPCError {
63+
System.out.println("====> task cancel request received");
64+
Task task = context.getTask();
65+
66+
if (task.getStatus().state() == TaskState.CANCELED) {
67+
System.out.println("====> task already canceled");
68+
throw new TaskNotCancelableError();
69+
}
70+
71+
if (task.getStatus().state() == TaskState.COMPLETED) {
72+
System.out.println("====> task already completed");
73+
throw new TaskNotCancelableError();
74+
}
75+
76+
TaskUpdater updater = new TaskUpdater(context, eventQueue);
77+
updater.cancel();
78+
eventQueue.enqueueEvent(new TaskStatusUpdateEvent.Builder()
79+
.taskId(task.getId())
80+
.contextId(task.getContextId())
81+
.status(new TaskStatus(TaskState.CANCELED))
82+
.isFinal(true)
83+
.build());
84+
85+
System.out.println("====> task canceled");
86+
}
87+
88+
/**
89+
* Cleanup method for proper resource management
90+
*/
91+
@PreDestroy
92+
public void cleanup() {
93+
System.out.println("====> shutting down task executor");
94+
}
95+
}
96+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
@NullMarked
2+
package io.a2a.tck.server;
3+
4+
import org.jspecify.annotations.NullMarked;
5+
6+
//The following had @Nullable annotation applied from JSpecify
7+
//AgentCardProducer.java getEnvOrDefault method,
8+
//AgentExecutorProducer.java execute method
9+
//
10+
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package org.a2aproject.sdk.compat03.transport.grpc.context;
2+
3+
import io.grpc.Context;
4+
5+
/**
6+
* Shared gRPC context keys for A2A protocol data.
7+
*
8+
* These keys provide access to gRPC context information similar to
9+
* Python's grpc.aio.ServicerContext, enabling rich context access
10+
* in service method implementations.
11+
*/
12+
public final class GrpcContextKeys {
13+
14+
/**
15+
* Context key for storing the X-A2A-Extensions header value.
16+
* Set by server interceptors and accessed by service handlers.
17+
*/
18+
public static final Context.Key<String> EXTENSIONS_HEADER_KEY =
19+
Context.key("x-a2a-extensions");
20+
21+
/**
22+
* Context key for storing the complete gRPC Metadata object.
23+
* Provides access to all request headers and metadata.
24+
*/
25+
public static final Context.Key<io.grpc.Metadata> METADATA_KEY =
26+
Context.key("grpc-metadata");
27+
28+
/**
29+
* Context key for storing the method name being called.
30+
* Equivalent to Python's context.method() functionality.
31+
*/
32+
public static final Context.Key<String> METHOD_NAME_KEY =
33+
Context.key("grpc-method-name");
34+
35+
/**
36+
* Context key for storing the peer information.
37+
* Provides access to client connection details.
38+
*/
39+
public static final Context.Key<String> PEER_INFO_KEY =
40+
Context.key("grpc-peer-info");
41+
42+
private GrpcContextKeys() {
43+
// Utility class
44+
}
45+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package org.a2aproject.sdk.compat03.transport.grpc.handler;
2+
3+
// TODO: Uncomment when server-common is ported
4+
// import org.a2aproject.sdk.compat03.server.ServerCallContext;
5+
// import io.grpc.stub.StreamObserver;
6+
//
7+
// public interface CallContextFactory {
8+
// <V> ServerCallContext create(StreamObserver<V> responseObserver);
9+
// }
10+
11+
/**
12+
* Placeholder stub - awaiting server-common port.
13+
*/
14+
public interface CallContextFactory {
15+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package org.a2aproject.sdk.compat03.transport.grpc.handler;
2+
3+
// TODO: Uncomment entire implementation when server-common is ported
4+
// This file has been temporarily stubbed out because it depends on server-common classes
5+
// that haven't been ported yet. See the original at:
6+
// /Users/kabir/sourcecontrol/AI/a2a-java-0.3.x/transport/grpc/src/main/java/io/a2a/transport/grpc/handler/GrpcHandler.java
7+
8+
/**
9+
* Placeholder stub for GrpcHandler.
10+
* The full implementation is commented out until server-common module is ported.
11+
*/
12+
public abstract class GrpcHandler {
13+
// Full implementation commented out - awaiting server-common port
14+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package org.a2aproject.sdk.compat03.transport.grpc.handler;
2+
3+
import org.junit.jupiter.api.Disabled;
4+
5+
// TODO: Uncomment entire test class when server-common is ported
6+
// This file has been temporarily stubbed out because it depends on server-common classes
7+
// that haven't been ported yet. See the original at:
8+
// /Users/kabir/sourcecontrol/AI/a2a-java-0.3.x/transport/grpc/src/test/java/io/a2a/transport/grpc/handler/GrpcHandlerTest.java
9+
10+
/**
11+
* Placeholder stub for GrpcHandlerTest.
12+
* The full implementation is commented out until server-common module is ported.
13+
*/
14+
@Disabled("Disabled until server-common is ported")
15+
public class GrpcHandlerTest {
16+
// Full test implementation commented out - awaiting server-common port
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package org.a2aproject.sdk.compat03.transport.grpc.handler;
2+
3+
// TODO: Uncomment when server-common is ported
4+
5+
/**
6+
* Placeholder stub - awaiting server-common port.
7+
*/
8+
public class GrpcTestTransportMetadata {
9+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package org.a2aproject.sdk.compat03.transport.jsonrpc.context;
2+
3+
/**
4+
* Shared JSON-RPC context keys for A2A protocol data.
5+
*
6+
* These keys provide access to JSON-RPC context information,
7+
* enabling rich context access in service method implementations.
8+
*/
9+
public final class JSONRPCContextKeys {
10+
11+
/**
12+
* Context key for storing the headers.
13+
*/
14+
public static final String HEADERS_KEY = "headers";
15+
16+
/**
17+
* Context key for storing the method name being called.
18+
*/
19+
public static final String METHOD_NAME_KEY = "method";
20+
21+
private JSONRPCContextKeys() {
22+
// Utility class
23+
}
24+
}

0 commit comments

Comments
 (0)