Skip to content

Commit bfdee13

Browse files
authored
feat!: Remove hard dependency on MicroProfile Config from the core SDK (a2aproject#468)
Default values will now be used by default. You can supply your own by providing a CDI bean with a higher priority. Also, there is a new 2a-java-sdk-microprofile-config with the previous MicroProfile Config capabilities. If used, this will allow MicroProfile Config configurations of the properties. The reference implementations use this new module Fixes a2aproject#467 🦕
1 parent 870b82c commit bfdee13

File tree

20 files changed

+703
-35
lines changed

20 files changed

+703
-35
lines changed

README.md

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -232,11 +232,22 @@ public class WeatherAgentExecutorProducer {
232232
}
233233
```
234234

235-
### 4. Configure Executor Settings (Optional)
235+
### 4. Configuration System
236236

237-
The A2A Java SDK uses a dedicated executor for handling asynchronous operations like streaming subscriptions. By default, this executor is configured with a core pool size of 5 threads and a maximum pool size of 50 threads, optimized for I/O-bound operations.
237+
The A2A Java SDK uses a flexible configuration system that works across different frameworks.
238238

239-
You can customize the executor settings in your `application.properties`:
239+
**Default behavior:** Configuration values come from `META-INF/a2a-defaults.properties` files on the classpath (provided by core modules and extras). These defaults work out of the box without any additional setup.
240+
241+
**Customizing configuration:**
242+
- **Quarkus/MicroProfile Config users**: Add the [`microprofile-config`](integrations/microprofile-config/README.md) integration to override defaults via `application.properties`, environment variables, or system properties
243+
- **Spring/other frameworks**: See the [integration module README](integrations/microprofile-config/README.md#custom-config-providers) for how to implement a custom `A2AConfigProvider`
244+
- **Reference implementations**: Already include the MicroProfile Config integration
245+
246+
#### Configuration Properties
247+
248+
**Executor Settings** (Optional)
249+
250+
The SDK uses a dedicated executor for async operations like streaming. Default: 5 core threads, 50 max threads.
240251

241252
```properties
242253
# Core thread pool size for the @Internal executor (default: 5)
@@ -249,20 +260,23 @@ a2a.executor.max-pool-size=50
249260
a2a.executor.keep-alive-seconds=60
250261
```
251262

252-
**Why this matters:**
253-
- **Streaming Performance**: The executor handles streaming subscriptions. Too few threads can cause timeouts under concurrent load.
254-
- **Resource Management**: The dedicated executor prevents streaming operations from competing with the ForkJoinPool used by other async tasks.
255-
- **Concurrency**: In production environments with high concurrent streaming requests, increase the pool sizes accordingly.
263+
**Blocking Call Timeouts** (Optional)
256264

257-
**Default Configuration:**
258265
```properties
259-
# These are the defaults - no need to set unless you want different values
260-
a2a.executor.core-pool-size=5
261-
a2a.executor.max-pool-size=50
262-
a2a.executor.keep-alive-seconds=60
266+
# Timeout for agent execution in blocking calls (default: 30 seconds)
267+
a2a.blocking.agent.timeout.seconds=30
268+
269+
# Timeout for event consumption in blocking calls (default: 5 seconds)
270+
a2a.blocking.consumption.timeout.seconds=5
263271
```
264272

265-
**Note:** The reference server implementations automatically configure this executor. If you're creating a custom server integration, ensure you provide an `@Internal Executor` bean for optimal streaming performance.
273+
**Why this matters:**
274+
- **Streaming Performance**: The executor handles streaming subscriptions. Too few threads can cause timeouts under concurrent load.
275+
- **Resource Management**: The dedicated executor prevents streaming operations from competing with the ForkJoinPool.
276+
- **Concurrency**: In production with high concurrent streaming, increase pool sizes accordingly.
277+
- **Agent Timeouts**: LLM-based agents may need longer timeouts (60-120s) compared to simple agents.
278+
279+
**Note:** The reference server implementations (Quarkus-based) automatically include the MicroProfile Config integration, so properties work out of the box in `application.properties`.
266280

267281
## A2A Client
268282

boms/extras/src/it/extras-usage-test/pom.xml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,10 @@
7474
<groupId>io.github.a2asdk</groupId>
7575
<artifactId>a2a-java-sdk-server-common</artifactId>
7676
</dependency>
77+
<dependency>
78+
<groupId>io.github.a2asdk</groupId>
79+
<artifactId>a2a-java-sdk-microprofile-config</artifactId>
80+
</dependency>
7781
<dependency>
7882
<groupId>io.github.a2asdk</groupId>
7983
<artifactId>a2a-java-sdk-client</artifactId>
@@ -102,6 +106,12 @@
102106
<groupId>io.github.a2asdk</groupId>
103107
<artifactId>a2a-java-sdk-transport-rest</artifactId>
104108
</dependency>
109+
110+
<!-- Third-party dependencies needed for integration classes -->
111+
<dependency>
112+
<groupId>org.eclipse.microprofile.config</groupId>
113+
<artifactId>microprofile-config-api</artifactId>
114+
</dependency>
105115
</dependencies>
106116

107117
<build>

boms/reference/src/it/reference-usage-test/pom.xml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,10 @@
6969
<groupId>io.github.a2asdk</groupId>
7070
<artifactId>a2a-java-sdk-server-common</artifactId>
7171
</dependency>
72+
<dependency>
73+
<groupId>io.github.a2asdk</groupId>
74+
<artifactId>a2a-java-sdk-microprofile-config</artifactId>
75+
</dependency>
7276
<dependency>
7377
<groupId>io.github.a2asdk</groupId>
7478
<artifactId>a2a-java-sdk-client</artifactId>
@@ -86,6 +90,12 @@
8690
<artifactId>a2a-java-sdk-transport-jsonrpc</artifactId>
8791
</dependency>
8892

93+
<!-- Third-party dependencies needed for integration classes -->
94+
<dependency>
95+
<groupId>org.eclipse.microprofile.config</groupId>
96+
<artifactId>microprofile-config-api</artifactId>
97+
</dependency>
98+
8999
<!-- Quarkus modules - should come from imported Quarkus BOM -->
90100
<dependency>
91101
<groupId>io.quarkus</groupId>

boms/sdk/pom.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,13 @@
5252
<version>${project.version}</version>
5353
</dependency>
5454

55+
<!-- Integration modules -->
56+
<dependency>
57+
<groupId>${project.groupId}</groupId>
58+
<artifactId>a2a-java-sdk-microprofile-config</artifactId>
59+
<version>${project.version}</version>
60+
</dependency>
61+
5562
<!-- Client modules -->
5663
<dependency>
5764
<groupId>${project.groupId}</groupId>

boms/sdk/src/it/sdk-usage-test/pom.xml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,12 @@
6161
<artifactId>a2a-java-sdk-server-common</artifactId>
6262
</dependency>
6363

64+
<!-- Integration modules -->
65+
<dependency>
66+
<groupId>io.github.a2asdk</groupId>
67+
<artifactId>a2a-java-sdk-microprofile-config</artifactId>
68+
</dependency>
69+
6470
<!-- Client modules -->
6571
<dependency>
6672
<groupId>io.github.a2asdk</groupId>
@@ -122,6 +128,10 @@
122128
<groupId>io.grpc</groupId>
123129
<artifactId>grpc-stub</artifactId>
124130
</dependency>
131+
<dependency>
132+
<groupId>org.eclipse.microprofile.config</groupId>
133+
<artifactId>microprofile-config-api</artifactId>
134+
</dependency>
125135
</dependencies>
126136

127137
<build>

extras/task-store-database-jpa/src/main/java/io/a2a/extras/taskstore/database/jpa/JpaDatabaseTaskStore.java

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import java.util.List;
88
import java.util.stream.Stream;
99

10+
import jakarta.annotation.PostConstruct;
1011
import jakarta.annotation.Priority;
1112
import jakarta.enterprise.context.ApplicationScoped;
1213
import jakarta.enterprise.event.Event;
@@ -19,14 +20,14 @@
1920

2021
import com.fasterxml.jackson.core.JsonProcessingException;
2122
import io.a2a.extras.common.events.TaskFinalizedEvent;
23+
import io.a2a.server.config.A2AConfigProvider;
2224
import io.a2a.server.tasks.TaskStateProvider;
2325
import io.a2a.server.tasks.TaskStore;
2426
import io.a2a.spec.Artifact;
2527
import io.a2a.spec.ListTasksParams;
2628
import io.a2a.spec.ListTasksResult;
2729
import io.a2a.spec.Message;
2830
import io.a2a.spec.Task;
29-
import org.eclipse.microprofile.config.inject.ConfigProperty;
3031
import org.slf4j.Logger;
3132
import org.slf4j.LoggerFactory;
3233

@@ -44,9 +45,24 @@ public class JpaDatabaseTaskStore implements TaskStore, TaskStateProvider {
4445
Event<TaskFinalizedEvent> taskFinalizedEvent;
4546

4647
@Inject
47-
@ConfigProperty(name = "a2a.replication.grace-period-seconds", defaultValue = "15")
48+
A2AConfigProvider configProvider;
49+
50+
/**
51+
* Grace period for task finalization in replicated scenarios (seconds).
52+
* After a task reaches a final state, this is the minimum time to wait before cleanup
53+
* to allow replicated events to arrive and be processed.
54+
* <p>
55+
* Property: {@code a2a.replication.grace-period-seconds}<br>
56+
* Default: 15<br>
57+
* Note: Property override requires a configurable {@link A2AConfigProvider} on the classpath.
58+
*/
4859
long gracePeriodSeconds;
4960

61+
@PostConstruct
62+
void initConfig() {
63+
gracePeriodSeconds = Long.parseLong(configProvider.getValue("a2a.replication.grace-period-seconds"));
64+
}
65+
5066
@Transactional
5167
@Override
5268
public void save(Task task) {
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# A2A JPA Database Task Store Default Configuration
2+
3+
# Grace period for task finalization in replicated scenarios (seconds)
4+
# After a task reaches a final state, this is the minimum time to wait before cleanup
5+
# to allow replicated events to arrive and be processed
6+
a2a.replication.grace-period-seconds=15
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
# A2A Java SDK - MicroProfile Config Integration
2+
3+
This optional integration module provides MicroProfile Config support for the A2A Java SDK configuration system.
4+
5+
## Overview
6+
7+
The A2A Java SDK core uses the `A2AConfigProvider` interface for configuration, with default values loaded from `META-INF/a2a-defaults.properties` files on the classpath.
8+
9+
This module provides `MicroProfileConfigProvider`, which integrates with MicroProfile Config to allow configuration via:
10+
- `application.properties`
11+
- Environment variables
12+
- System properties (`-D` flags)
13+
- Custom ConfigSources
14+
15+
## Quick Start
16+
17+
### 1. Add Dependency
18+
19+
```xml
20+
<dependency>
21+
<groupId>io.github.a2asdk</groupId>
22+
<artifactId>a2a-java-sdk-microprofile-config</artifactId>
23+
<version>${io.a2a.sdk.version}</version>
24+
</dependency>
25+
```
26+
27+
### 2. Configure Properties
28+
29+
Once the dependency is added, you can override any A2A configuration property:
30+
31+
**application.properties:**
32+
```properties
33+
# Executor configuration
34+
a2a.executor.core-pool-size=10
35+
a2a.executor.max-pool-size=100
36+
37+
# Timeout configuration
38+
a2a.blocking.agent.timeout.seconds=60
39+
a2a.blocking.consumption.timeout.seconds=10
40+
```
41+
42+
**Environment variables:**
43+
```bash
44+
export A2A_EXECUTOR_CORE_POOL_SIZE=10
45+
export A2A_BLOCKING_AGENT_TIMEOUT_SECONDS=60
46+
```
47+
48+
**System properties:**
49+
```bash
50+
java -Da2a.executor.core-pool-size=10 -jar your-app.jar
51+
```
52+
53+
## How It Works
54+
55+
The `MicroProfileConfigProvider` implementation:
56+
57+
1. **First tries MicroProfile Config** - Checks `application.properties`, environment variables, system properties, and custom ConfigSources
58+
2. **Falls back to defaults** - If not found, uses values from `META-INF/a2a-defaults.properties` provided by core modules and extras
59+
3. **Priority 50** - Can be overridden by custom providers with higher priority
60+
61+
## Configuration Fallback Chain
62+
63+
```
64+
MicroProfile Config Sources (application.properties, env vars, -D flags)
65+
↓ (not found?)
66+
DefaultValuesConfigProvider
67+
→ Scans classpath for ALL META-INF/a2a-defaults.properties files
68+
→ Merges all discovered properties together
69+
→ Throws exception if duplicate keys found
70+
↓ (property exists?)
71+
Return merged default value
72+
↓ (not found?)
73+
IllegalArgumentException
74+
```
75+
76+
**Note**: All `META-INF/a2a-defaults.properties` files (from server-common, extras modules, etc.) are loaded and merged together by `DefaultValuesConfigProvider` at startup. This is not a sequential fallback chain, but a single merged set of defaults.
77+
78+
## Available Configuration Properties
79+
80+
See the [main README](../../README.md#configuration-system) for a complete list of configuration properties.
81+
82+
## Framework Compatibility
83+
84+
This module works with any MicroProfile Config implementation:
85+
86+
- **Quarkus** - Built-in MicroProfile Config support
87+
- **Helidon** - Built-in MicroProfile Config support
88+
- **Open Liberty** - Built-in MicroProfile Config support
89+
- **WildFly/JBoss EAP** - Add `smallrye-config` dependency
90+
- **Other Jakarta EE servers** - Add MicroProfile Config implementation
91+
92+
## Custom Config Providers
93+
94+
If you're using a different framework (Spring, Micronaut, etc.), you can implement your own `A2AConfigProvider`:
95+
96+
```java
97+
import io.a2a.server.config.A2AConfigProvider;
98+
import io.a2a.server.config.DefaultValuesConfigProvider;
99+
import jakarta.enterprise.context.ApplicationScoped;
100+
import jakarta.enterprise.inject.Alternative;
101+
import jakarta.annotation.Priority;
102+
import jakarta.inject.Inject;
103+
104+
@ApplicationScoped
105+
@Alternative
106+
@Priority(100) // Higher than MicroProfileConfigProvider's priority of 50
107+
public class OtherEnvironmentConfigProvider implements A2AConfigProvider {
108+
109+
@Inject
110+
Environment env;
111+
112+
@Inject
113+
DefaultValuesConfigProvider defaultValues;
114+
115+
@Override
116+
public String getValue(String name) {
117+
String value = env.getProperty(name);
118+
if (value != null) {
119+
return value;
120+
}
121+
// Fallback to defaults
122+
return defaultValues.getValue(name);
123+
}
124+
125+
@Override
126+
public Optional<String> getOptionalValue(String name) {
127+
String value = env.getProperty(name);
128+
if (value != null) {
129+
return Optional.of(value);
130+
}
131+
return defaultValues.getOptionalValue(name);
132+
}
133+
}
134+
```
135+
136+
## Implementation Details
137+
138+
- **Package**: `io.a2a.integrations.microprofile`
139+
- **Class**: `MicroProfileConfigProvider`
140+
- **Priority**: 50 (can be overridden)
141+
- **Scope**: `@ApplicationScoped`
142+
- **Dependencies**: MicroProfile Config API, A2A SDK server-common
143+
144+
## Reference Implementations
145+
146+
The A2A Java SDK reference implementations (Quarkus-based) automatically include this integration module, so MicroProfile Config properties work out of the box.
147+
148+
If you're building a custom server implementation, add this dependency to enable property-based configuration.

0 commit comments

Comments
 (0)