Skip to content

Commit ab36702

Browse files
committed
Integrate gRPC with server
1 parent 9cc50c9 commit ab36702

File tree

11 files changed

+1577
-22
lines changed

11 files changed

+1577
-22
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<beans xmlns="https://jakarta.ee/xml/ns/jakartaee"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/beans_4_0.xsd"
5+
version="4.0" bean-discovery-mode="all">
6+
</beans>

pom.xml

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,12 @@
5252
<jakarta.inject.jakarta.inject-api.version>2.0.1</jakarta.inject.jakarta.inject-api.version>
5353
<jakarta.json-api.version>2.1.3</jakarta.json-api.version>
5454
<jakarta.ws.rs-api.version>3.1.0</jakarta.ws.rs-api.version>
55-
<junit.version>5.12.2</junit.version>
55+
<junit.version>5.13.4</junit.version>
5656
<mockito-core.version>5.17.0</mockito-core.version>
5757
<mockserver.version>5.15.0</mockserver.version>
5858
<mutiny-zero.version>1.1.1</mutiny-zero.version>
5959
<protobuf.version>4.31.1</protobuf.version>
60-
<quarkus.platform.version>3.22.3</quarkus.platform.version>
60+
<quarkus.platform.version>3.24.5</quarkus.platform.version>
6161
<rest-assured.version>5.5.1</rest-assured.version>
6262
<slf4j.version>2.0.17</slf4j.version>
6363
<logback.version>1.5.18</logback.version>
@@ -205,15 +205,6 @@
205205
<artifactId>quarkus-maven-plugin</artifactId>
206206
<extensions>true</extensions>
207207
<version>${quarkus.platform.version}</version>
208-
<executions>
209-
<execution>
210-
<goals>
211-
<goal>build</goal>
212-
<goal>generate-code</goal>
213-
<goal>generate-code-tests</goal>
214-
</goals>
215-
</execution>
216-
</executions>
217208
</plugin>
218209
<plugin>
219210
<groupId>org.sonatype.central</groupId>
@@ -290,6 +281,7 @@
290281
<module>grpc</module>
291282
<module>spec</module>
292283
<module>client</module>
284+
<module>reference-grpc</module>
293285
<module>reference-impl</module>
294286
<module>tck</module>
295287
<module>examples/helloworld</module>

reference-grpc/pom.xml

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
<?xml version="1.0"?>
2+
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"
3+
xmlns="http://maven.apache.org/POM/4.0.0"
4+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
5+
<modelVersion>4.0.0</modelVersion>
6+
<parent>
7+
<groupId>io.github.a2asdk</groupId>
8+
<artifactId>a2a-java-sdk-parent</artifactId>
9+
<version>0.2.6.Beta1-SNAPSHOT</version>
10+
<relativePath>../pom.xml</relativePath>
11+
</parent>
12+
13+
<artifactId>reference-grpc</artifactId>
14+
<name>Java A2A gRPC Reference Server</name>
15+
<description>Java SDK for the Agent2Agent Protocol (A2A) - A2A gRPC Reference Server (based on Quarkus)</description>
16+
17+
<dependencies>
18+
<dependency>
19+
<groupId>${project.groupId}</groupId>
20+
<artifactId>a2a-java-sdk-grpc</artifactId>
21+
<version>${project.version}</version>
22+
</dependency>
23+
<dependency>
24+
<groupId>${project.groupId}</groupId>
25+
<artifactId>a2a-java-sdk-server-common</artifactId>
26+
<version>${project.version}</version>
27+
</dependency>
28+
<dependency>
29+
<groupId>${project.groupId}</groupId>
30+
<artifactId>a2a-java-sdk-tests-server-common</artifactId>
31+
<version>${project.version}</version>
32+
<scope>provided</scope>
33+
</dependency>
34+
<dependency>
35+
<groupId>${project.groupId}</groupId>
36+
<artifactId>a2a-java-sdk-tests-server-common</artifactId>
37+
<type>test-jar</type>
38+
<scope>test</scope>
39+
<version>${project.version}</version>
40+
</dependency>
41+
42+
<dependency>
43+
<groupId>io.quarkus</groupId>
44+
<artifactId>quarkus-grpc</artifactId>
45+
</dependency>
46+
<dependency>
47+
<groupId>io.quarkus</groupId>
48+
<artifactId>quarkus-rest</artifactId>
49+
</dependency>
50+
<dependency>
51+
<groupId>io.quarkus</groupId>
52+
<artifactId>quarkus-junit5</artifactId>
53+
<scope>test</scope>
54+
</dependency>
55+
<dependency>
56+
<groupId>org.assertj</groupId>
57+
<artifactId>assertj-core</artifactId>
58+
<version>3.25.3</version>
59+
<scope>test</scope>
60+
</dependency>
61+
<dependency>
62+
<groupId>com.google.api.grpc</groupId>
63+
<artifactId>proto-google-common-protos</artifactId>
64+
</dependency>
65+
<dependency>
66+
<groupId>com.google.protobuf</groupId>
67+
<artifactId>protobuf-java</artifactId>
68+
</dependency>
69+
<dependency>
70+
<groupId>io.grpc</groupId>
71+
<artifactId>grpc-protobuf</artifactId>
72+
</dependency>
73+
<dependency>
74+
<groupId>io.grpc</groupId>
75+
<artifactId>grpc-stub</artifactId>
76+
</dependency>
77+
</dependencies>
78+
79+
</project>
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package io.a2a.server.grpc.quarkus;
2+
3+
import jakarta.enterprise.inject.Instance;
4+
import jakarta.inject.Inject;
5+
6+
import io.a2a.grpc.SendMessageRequest;
7+
import io.a2a.grpc.SendMessageResponse;
8+
import io.a2a.grpc.StreamResponse;
9+
import io.a2a.grpc.Task;
10+
import io.a2a.grpc.TaskState;
11+
import io.a2a.grpc.TaskStatus;
12+
import io.a2a.grpc.TaskStatusUpdateEvent;
13+
import io.a2a.server.PublicAgentCard;
14+
import io.a2a.server.requesthandlers.GrpcHandler;
15+
import io.a2a.server.requesthandlers.RequestHandler;
16+
import io.a2a.spec.AgentCard;
17+
import io.grpc.stub.StreamObserver;
18+
import io.quarkus.grpc.GrpcService;
19+
20+
@GrpcService
21+
public class QuarkusGrpcHandler extends GrpcHandler {
22+
23+
@Inject
24+
public QuarkusGrpcHandler(@PublicAgentCard AgentCard agentCard, RequestHandler requestHandler) {
25+
super(agentCard, requestHandler);
26+
}
27+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
quarkus.grpc.clients.a2-a-service.host=localhost
2+
quarkus.grpc.clients.a2-a-service.port=9001
3+
4+
# The GrpcHandler @ApplicationScoped annotation is not compatible with Quarkus
5+
quarkus.arc.exclude-types=io.a2a.server.requesthandlers.GrpcHandler
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
package io.a2a.server.grpc.quarkus;
2+
3+
import static jakarta.ws.rs.core.MediaType.TEXT_PLAIN;
4+
5+
import java.util.concurrent.atomic.AtomicInteger;
6+
7+
import jakarta.annotation.PostConstruct;
8+
import jakarta.enterprise.context.ApplicationScoped;
9+
import jakarta.inject.Inject;
10+
import jakarta.ws.rs.Consumes;
11+
import jakarta.ws.rs.DELETE;
12+
import jakarta.ws.rs.GET;
13+
import jakarta.ws.rs.POST;
14+
import jakarta.ws.rs.Path;
15+
import jakarta.ws.rs.PathParam;
16+
import jakarta.ws.rs.Produces;
17+
import jakarta.ws.rs.core.HttpHeaders;
18+
import jakarta.ws.rs.core.MediaType;
19+
import jakarta.ws.rs.core.Response;
20+
21+
import io.a2a.server.apps.common.TestUtilsBean;
22+
import io.a2a.server.requesthandlers.GrpcHandler;
23+
import io.a2a.spec.PushNotificationConfig;
24+
import io.a2a.spec.Task;
25+
import io.a2a.spec.TaskArtifactUpdateEvent;
26+
import io.a2a.spec.TaskStatusUpdateEvent;
27+
import io.a2a.util.Utils;
28+
29+
@Path("/test")
30+
@ApplicationScoped
31+
public class A2ATestResource {
32+
@Inject
33+
TestUtilsBean testUtilsBean;
34+
35+
private final AtomicInteger streamingSubscribedCount = new AtomicInteger(0);
36+
37+
@PostConstruct
38+
public void init() {
39+
GrpcHandler.setStreamingSubscribedRunnable(streamingSubscribedCount::incrementAndGet);
40+
}
41+
42+
43+
@POST
44+
@Path("/task")
45+
@Consumes(MediaType.APPLICATION_JSON)
46+
public Response saveTask(String body) throws Exception {
47+
Task task = Utils.OBJECT_MAPPER.readValue(body, Task.class);
48+
testUtilsBean.saveTask(task);
49+
return Response.ok().build();
50+
}
51+
52+
@GET
53+
@Path("/task/{taskId}")
54+
public Response getTask(@PathParam("taskId") String taskId) throws Exception {
55+
Task task = testUtilsBean.getTask(taskId);
56+
if (task == null) {
57+
return Response.status(404).build();
58+
}
59+
return Response.ok()
60+
.entity(Utils.OBJECT_MAPPER.writeValueAsString(task))
61+
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)
62+
.build();
63+
}
64+
65+
@DELETE
66+
@Path("/task/{taskId}")
67+
public Response deleteTask(@PathParam("taskId") String taskId) {
68+
Task task = testUtilsBean.getTask(taskId);
69+
if (task == null) {
70+
return Response.status(404).build();
71+
}
72+
testUtilsBean.deleteTask(taskId);
73+
return Response.ok()
74+
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)
75+
.build();
76+
}
77+
78+
@POST
79+
@Path("/queue/ensure/{taskId}")
80+
public Response ensureQueue(@PathParam("taskId") String taskId) {
81+
testUtilsBean.ensureQueue(taskId);
82+
return Response.ok().build();
83+
}
84+
85+
@POST
86+
@Path("/queue/enqueueTaskStatusUpdateEvent/{taskId}")
87+
public Response enqueueTaskStatusUpdateEvent(@PathParam("taskId") String taskId, String body) throws Exception {
88+
TaskStatusUpdateEvent event = Utils.OBJECT_MAPPER.readValue(body, TaskStatusUpdateEvent.class);
89+
testUtilsBean.enqueueEvent(taskId, event);
90+
return Response.ok().build();
91+
}
92+
93+
@POST
94+
@Path("/queue/enqueueTaskArtifactUpdateEvent/{taskId}")
95+
public Response enqueueTaskArtifactUpdateEvent(@PathParam("taskId") String taskId, String body) throws Exception {
96+
TaskArtifactUpdateEvent event = Utils.OBJECT_MAPPER.readValue(body, TaskArtifactUpdateEvent.class);
97+
testUtilsBean.enqueueEvent(taskId, event);
98+
return Response.ok().build();
99+
}
100+
101+
@GET
102+
@Path("/streamingSubscribedCount")
103+
@Produces(TEXT_PLAIN)
104+
public Response getStreamingSubscribedCount() {
105+
return Response.ok(String.valueOf(streamingSubscribedCount.get()), TEXT_PLAIN).build();
106+
}
107+
108+
@DELETE
109+
@Path("/task/{taskId}/config/{configId}")
110+
public Response deleteTaskPushNotificationConfig(@PathParam("taskId") String taskId, @PathParam("configId") String configId) {
111+
Task task = testUtilsBean.getTask(taskId);
112+
if (task == null) {
113+
return Response.status(404).build();
114+
}
115+
testUtilsBean.deleteTaskPushNotificationConfig(taskId, configId);
116+
return Response.ok()
117+
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)
118+
.build();
119+
}
120+
121+
@POST
122+
@Path("/task/{taskId}")
123+
@Consumes(MediaType.APPLICATION_JSON)
124+
public Response savePushNotificationConfigInStore(@PathParam("taskId") String taskId, String body) throws Exception {
125+
PushNotificationConfig notificationConfig = Utils.OBJECT_MAPPER.readValue(body, PushNotificationConfig.class);
126+
if (notificationConfig == null) {
127+
return Response.status(404).build();
128+
}
129+
testUtilsBean.saveTaskPushNotificationConfig(taskId, notificationConfig);
130+
return Response.ok().build();
131+
}
132+
}

0 commit comments

Comments
 (0)