Skip to content

Commit 35fad3e

Browse files
committed
Add gRPC support
1 parent 04cf676 commit 35fad3e

File tree

16 files changed

+2005
-3
lines changed

16 files changed

+2005
-3
lines changed

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.CallContextFactory;
12+
import io.a2a.server.requesthandlers.RequestHandler;
13+
import io.a2a.spec.AgentCard;
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.grpc.handler.GrpcHandler;
4+
import io.a2a.server.requesthandlers.CallContextFactory;
5+
import io.a2a.server.requesthandlers.RequestHandler;
6+
import io.a2a.spec.AgentCard;
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
@@ -25,11 +25,11 @@
2525

2626
import com.fasterxml.jackson.core.JsonParseException;
2727
import com.fasterxml.jackson.databind.JsonMappingException;
28+
import io.a2a.jsonrpc.handler.JSONRPCHandler;
2829
import io.a2a.server.ExtendedAgentCard;
2930
import io.a2a.server.ServerCallContext;
3031
import io.a2a.server.auth.UnauthenticatedUser;
3132
import io.a2a.server.auth.User;
32-
import io.a2a.server.requesthandlers.JSONRPCHandler;
3333
import io.a2a.server.util.async.Internal;
3434
import io.a2a.spec.AgentCard;
3535
import io.a2a.spec.CancelTaskRequest;
@@ -60,7 +60,7 @@
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 & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@
4848
<version.sdk>${project.version}</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,8 @@
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+
<!-- Use SNAPSHOT of gRPC feature pack to get the right versions -->
64+
<version.wildfly.grpc>0.1.12-SNAPSHOT</version.wildfly.grpc>
6165

6266
<jboss.home>${project.build.directory}${file.separator}wildfly</jboss.home>
6367
<arquillian.java.vm.args></arquillian.java.vm.args>
@@ -117,6 +121,21 @@
117121
<artifactId>a2a-java-sdk-spec</artifactId>
118122
<version>${version.sdk}</version>
119123
</dependency>
124+
<dependency>
125+
<groupId>io.github.a2asdk</groupId>
126+
<artifactId>a2a-java-sdk-spec-grpc</artifactId>
127+
<version>${version.sdk}</version>
128+
</dependency>
129+
<dependency>
130+
<groupId>io.github.a2asdk</groupId>
131+
<artifactId>a2a-java-sdk-transport-grpc</artifactId>
132+
<version>${version.sdk}</version>
133+
</dependency>
134+
<dependency>
135+
<groupId>io.github.a2asdk</groupId>
136+
<artifactId>a2a-java-sdk-transport-jsonrpc</artifactId>
137+
<version>${version.sdk}</version>
138+
</dependency>
120139
<dependency>
121140
<groupId>io.github.a2asdk</groupId>
122141
<artifactId>a2a-tck-server</artifactId>
@@ -162,6 +181,37 @@
162181
<version>${version.hamcrest}</version>
163182
<scope>test</scope>
164183
</dependency>
184+
<!-- gRPC dependencies -->
185+
<dependency>
186+
<groupId>io.grpc</groupId>
187+
<artifactId>grpc-protobuf</artifactId>
188+
<version>${version.grpc}</version>
189+
</dependency>
190+
<dependency>
191+
<groupId>io.grpc</groupId>
192+
<artifactId>grpc-stub</artifactId>
193+
<version>${version.grpc}</version>
194+
</dependency>
195+
<dependency>
196+
<groupId>io.grpc</groupId>
197+
<artifactId>grpc-netty</artifactId>
198+
<version>${version.grpc}</version>
199+
</dependency>
200+
<dependency>
201+
<groupId>io.grpc</groupId>
202+
<artifactId>grpc-netty-shaded</artifactId>
203+
<version>${version.grpc}</version>
204+
</dependency>
205+
<dependency>
206+
<groupId>com.google.protobuf</groupId>
207+
<artifactId>protobuf-java</artifactId>
208+
<version>4.31.1</version>
209+
</dependency>
210+
<dependency>
211+
<groupId>com.google.guava</groupId>
212+
<artifactId>failureaccess</artifactId>
213+
<version>1.0.2</version>
214+
</dependency>
165215
</dependencies>
166216
</dependencyManagement>
167217
<build>
@@ -249,9 +299,11 @@
249299
</pluginManagement>
250300
</build>
251301
<modules>
302+
<module>grpc</module>
252303
<module>impl</module>
253304
<module>tck</module>
254305
<module>tests</module>
306+
<module>tests-grpc</module>
255307
</modules>
256308
<profiles>
257309
<profile>

0 commit comments

Comments
 (0)