Skip to content

Commit 2a732dd

Browse files
committed
Add gRPC support
1 parent 04cf676 commit 2a732dd

File tree

19 files changed

+2025
-14
lines changed

19 files changed

+2025
-14
lines changed

a2a-java-sdk-server-jakarta.iml

Lines changed: 0 additions & 9 deletions
This file was deleted.

grpc/pom.xml

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
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+
7+
<parent>
8+
<groupId>org.wildfly.extras.a2a</groupId>
9+
<artifactId>a2a-java-sdk-server-jakarta-parent</artifactId>
10+
<version>0.2.6.Beta1-SNAPSHOT</version>
11+
</parent>
12+
13+
<artifactId>a2a-java-sdk-server-jakarta-grpc</artifactId>
14+
<packaging>jar</packaging>
15+
16+
<name>WildFly Extras - Java A2A SDK for Jakarta gRPC</name>
17+
<description>Java SDK for the Agent2Agent Protocol (A2A) - SDK - Jakarta - gRPC integration</description>
18+
19+
<dependencies>
20+
<!-- A2A spec-grpc dependency -->
21+
<dependency>
22+
<groupId>io.github.a2asdk</groupId>
23+
<artifactId>a2a-java-sdk-transport-grpc</artifactId>
24+
<version>${version.sdk}</version>
25+
<scope>provided</scope>
26+
</dependency>
27+
28+
<!-- Jakarta APIs needed for CDI and annotation support -->
29+
<dependency>
30+
<groupId>jakarta.annotation</groupId>
31+
<artifactId>jakarta.annotation-api</artifactId>
32+
<scope>provided</scope>
33+
</dependency>
34+
<dependency>
35+
<groupId>jakarta.enterprise</groupId>
36+
<artifactId>jakarta.enterprise.cdi-api</artifactId>
37+
<scope>provided</scope>
38+
</dependency>
39+
<dependency>
40+
<groupId>jakarta.inject</groupId>
41+
<artifactId>jakarta.inject-api</artifactId>
42+
<scope>provided</scope>
43+
</dependency>
44+
45+
<!-- A2A server common for @PublicAgentCard and RequestHandler -->
46+
<dependency>
47+
<groupId>io.github.a2asdk</groupId>
48+
<artifactId>a2a-java-sdk-server-common</artifactId>
49+
<version>${version.sdk}</version>
50+
<scope>provided</scope>
51+
</dependency>
52+
</dependencies>
53+
</project>
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package org.wildfly.extras.a2a.server.apps.grpc;
2+
3+
import jakarta.annotation.PreDestroy;
4+
import jakarta.enterprise.context.ApplicationScoped;
5+
import jakarta.enterprise.context.Initialized;
6+
import jakarta.enterprise.event.Observes;
7+
import jakarta.enterprise.inject.Instance;
8+
import jakarta.inject.Inject;
9+
10+
import io.a2a.server.PublicAgentCard;
11+
import io.a2a.server.requesthandlers.RequestHandler;
12+
import io.a2a.spec.AgentCard;
13+
import io.a2a.transport.grpc.handler.CallContextFactory;
14+
15+
/**
16+
* Bean initializer that observes application startup events.
17+
*
18+
* Since CDI is not available on gRPC threads, we capture the CDI beans
19+
* during application startup and store them statically for use by
20+
* the WildFly gRPC subsystem.
21+
*/
22+
@ApplicationScoped
23+
public class GrpcBeanInitializer {
24+
25+
@Inject
26+
@PublicAgentCard
27+
AgentCard agentCard;
28+
29+
@Inject
30+
RequestHandler requestHandler;
31+
32+
@Inject
33+
Instance<CallContextFactory> callContextFactory;
34+
35+
/**
36+
* Observes the application startup event to eagerly initialize the gRPC cache.
37+
*/
38+
public void onStartup(@Observes @Initialized(ApplicationScoped.class) Object init) {
39+
System.out.println("*** GrpcBeanInitializer.onStartup() called - ApplicationScoped initialized ***");
40+
try {
41+
// Cache CDI beans for gRPC threads to use since CDI is not available on those threads
42+
CallContextFactory ccf = callContextFactory.isUnsatisfied() ? null : callContextFactory.get();
43+
WildFlyGrpcHandler.setStaticBeans(agentCard, requestHandler, ccf);
44+
System.out.println("*** GrpcBeanInitializer successfully cached beans: agentCard=" + agentCard + ", requestHandler=" + requestHandler + ", callContextFactory=" + ccf + " ***");
45+
} catch (Exception e) {
46+
System.err.println("*** GrpcBeanInitializer.onStartup() failed: " + e.getMessage());
47+
e.printStackTrace();
48+
}
49+
}
50+
51+
@PreDestroy
52+
public void cleanup() {
53+
WildFlyGrpcHandler.setStaticBeans(null, null, null);
54+
}
55+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package org.wildfly.extras.a2a.server.apps.grpc;
2+
3+
import io.a2a.server.requesthandlers.RequestHandler;
4+
import io.a2a.spec.AgentCard;
5+
import io.a2a.transport.grpc.handler.CallContextFactory;
6+
import io.a2a.transport.grpc.handler.GrpcHandler;
7+
8+
/**
9+
* WildFly gRPC Handler that uses static cache for CDI beans.
10+
*
11+
* The WildFly gRPC subsystem instantiates this class directly using
12+
* reflection and the default constructor, bypassing CDI completely.
13+
*
14+
* Since CDI is not available on gRPC threads, we use static cache
15+
* populated during application startup when CDI is available.
16+
*/
17+
public class WildFlyGrpcHandler extends GrpcHandler {
18+
19+
// Static cache populated during application startup by GrpcBeanInitializer
20+
private static volatile AgentCard staticAgentCard;
21+
private static volatile RequestHandler staticRequestHandler;
22+
private static volatile CallContextFactory staticCallContextFactory;
23+
24+
public WildFlyGrpcHandler() {
25+
// Default constructor - the only one used by WildFly gRPC subsystem
26+
}
27+
28+
/**
29+
* Called by GrpcBeanInitializer during CDI initialization to cache beans
30+
* for use by gRPC threads where CDI is not available.
31+
*/
32+
static void setStaticBeans(AgentCard agentCard, RequestHandler requestHandler, CallContextFactory callContextFactory) {
33+
staticAgentCard = agentCard;
34+
staticRequestHandler = requestHandler;
35+
staticCallContextFactory = callContextFactory;
36+
}
37+
38+
@Override
39+
protected RequestHandler getRequestHandler() {
40+
if (staticRequestHandler == null) {
41+
throw new RuntimeException("RequestHandler not available. ApplicationStartup may not have run yet.");
42+
}
43+
return staticRequestHandler;
44+
}
45+
46+
@Override
47+
protected AgentCard getAgentCard() {
48+
if (staticAgentCard == null) {
49+
throw new RuntimeException("AgentCard not available. ApplicationStartup may not have run yet.");
50+
}
51+
return staticAgentCard;
52+
}
53+
54+
@Override
55+
protected CallContextFactory getCallContextFactory() {
56+
return staticCallContextFactory; // Can be null if not configured
57+
}
58+
}
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+
bean-discovery-mode="annotated">
6+
</beans>

impl/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
<dependencies>
2121
<dependency>
2222
<groupId>io.github.a2asdk</groupId>
23-
<artifactId>a2a-java-sdk-server-common</artifactId>
23+
<artifactId>a2a-java-sdk-transport-jsonrpc</artifactId>
2424
<exclusions>
2525
<exclusion>
2626
<groupId>jakarta.enterprise</groupId>

impl/src/main/java/org/wildfly/extras/a2a/server/apps/jakarta/A2AServerResource.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929
import io.a2a.server.ServerCallContext;
3030
import io.a2a.server.auth.UnauthenticatedUser;
3131
import io.a2a.server.auth.User;
32-
import io.a2a.server.requesthandlers.JSONRPCHandler;
3332
import io.a2a.server.util.async.Internal;
3433
import io.a2a.spec.AgentCard;
3534
import io.a2a.spec.CancelTaskRequest;
@@ -56,11 +55,12 @@
5655
import io.a2a.spec.StreamingJSONRPCRequest;
5756
import io.a2a.spec.TaskResubscriptionRequest;
5857
import io.a2a.spec.UnsupportedOperationError;
58+
import io.a2a.transport.jsonrpc.handler.JSONRPCHandler;
5959
import org.slf4j.Logger;
6060
import org.slf4j.LoggerFactory;
6161

6262
@Path("/")
63-
public class A2AServerResource {
63+
public class A2AServerResource {
6464

6565
private static final Logger LOGGER = LoggerFactory.getLogger(A2AServerResource.class);
6666

pom.xml

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,11 @@
4545
<properties>
4646
<jboss.home>${project.build.directory}${file.separator}wildfly</jboss.home>
4747
<version.wildfly>36.0.1.Final</version.wildfly>
48-
<version.sdk>${project.version}</version.sdk>
48+
<version.sdk>0.3.0.Beta1-SNAPSHOT</version.sdk>
4949
<!-- This needs to be same version as used by the sdk -->
5050
<version.mutiny-zero>1.1.1</version.mutiny-zero>
51+
<!-- gRPC version matching the a2a-java-sdk -->
52+
<version.grpc>1.73.0</version.grpc>
5153

5254
<version.jboss-commons-logging>1.0.0.Final</version.jboss-commons-logging>
5355
<version.hamcrest>2.2</version.hamcrest>
@@ -58,6 +60,7 @@
5860
<version.org.jboss.arquillian.jakarta>10.0.0.Final</version.org.jboss.arquillian.jakarta>
5961
<version.shrinkwrap.resolvers>3.3.4</version.shrinkwrap.resolvers>
6062
<version.rest-assured>5.5.5</version.rest-assured>
63+
<version.wildfly.grpc>0.1.12.Final</version.wildfly.grpc>
6164

6265
<jboss.home>${project.build.directory}${file.separator}wildfly</jboss.home>
6366
<arquillian.java.vm.args></arquillian.java.vm.args>
@@ -117,6 +120,21 @@
117120
<artifactId>a2a-java-sdk-spec</artifactId>
118121
<version>${version.sdk}</version>
119122
</dependency>
123+
<dependency>
124+
<groupId>io.github.a2asdk</groupId>
125+
<artifactId>a2a-java-sdk-spec-grpc</artifactId>
126+
<version>${version.sdk}</version>
127+
</dependency>
128+
<dependency>
129+
<groupId>io.github.a2asdk</groupId>
130+
<artifactId>a2a-java-sdk-transport-grpc</artifactId>
131+
<version>${version.sdk}</version>
132+
</dependency>
133+
<dependency>
134+
<groupId>io.github.a2asdk</groupId>
135+
<artifactId>a2a-java-sdk-transport-jsonrpc</artifactId>
136+
<version>${version.sdk}</version>
137+
</dependency>
120138
<dependency>
121139
<groupId>io.github.a2asdk</groupId>
122140
<artifactId>a2a-tck-server</artifactId>
@@ -162,6 +180,37 @@
162180
<version>${version.hamcrest}</version>
163181
<scope>test</scope>
164182
</dependency>
183+
<!-- gRPC dependencies -->
184+
<dependency>
185+
<groupId>io.grpc</groupId>
186+
<artifactId>grpc-protobuf</artifactId>
187+
<version>${version.grpc}</version>
188+
</dependency>
189+
<dependency>
190+
<groupId>io.grpc</groupId>
191+
<artifactId>grpc-stub</artifactId>
192+
<version>${version.grpc}</version>
193+
</dependency>
194+
<dependency>
195+
<groupId>io.grpc</groupId>
196+
<artifactId>grpc-netty</artifactId>
197+
<version>${version.grpc}</version>
198+
</dependency>
199+
<dependency>
200+
<groupId>io.grpc</groupId>
201+
<artifactId>grpc-netty-shaded</artifactId>
202+
<version>${version.grpc}</version>
203+
</dependency>
204+
<dependency>
205+
<groupId>com.google.protobuf</groupId>
206+
<artifactId>protobuf-java</artifactId>
207+
<version>4.31.1</version>
208+
</dependency>
209+
<dependency>
210+
<groupId>com.google.guava</groupId>
211+
<artifactId>failureaccess</artifactId>
212+
<version>1.0.2</version>
213+
</dependency>
165214
</dependencies>
166215
</dependencyManagement>
167216
<build>
@@ -249,9 +298,11 @@
249298
</pluginManagement>
250299
</build>
251300
<modules>
301+
<module>grpc</module>
252302
<module>impl</module>
253303
<module>tck</module>
254304
<module>tests</module>
305+
<module>tests-grpc</module>
255306
</modules>
256307
<profiles>
257308
<profile>

0 commit comments

Comments
 (0)