# Adding AWS AppConfig Support to avaje-config
This guide provides step-by-step instructions for integrating **AWS AppConfig** with avaje-config to enable dynamic configuration management. AWS AppConfig allows you to update application configuration without redeploying your service.
## What is AWS AppConfig?
[AWS AppConfig](https://docs.aws.amazon.com/appconfig/latest/userguide/what-is-appconfig.html) is an AWS service that helps you quickly and safely deploy application configurations across your infrastructure. With avaje-config's AWS AppConfig plugin, you can:
- Load configuration from AWS AppConfig on startup
- Automatically poll for configuration changes at runtime
- Dynamically update application behavior without redeployment
- Use YAML or properties format for configuration
- Maintain local defaults while leveraging remote configuration
## Prerequisites
- **AWS AppConfig Setup**: You must have:
- An AWS AppConfig application configured
- An environment within the application
- A configuration profile with your configuration data
- AWS credentials available to your application (via IAM roles, environment variables, or configuration)
- **Existing avaje-config Setup**: Follow the [Adding avaje-config to Your Project](adding-avaje-config.md) guide first.
## Step 1: Add AWS AppConfig Dependency
Add the avaje-aws-appconfig dependency to your `pom.xml`:
```xml
io.avaje
avaje-aws-appconfig
1.0
```
Replace `1.0` with the latest version from [Maven Central](https://mvnrepository.com/artifact/io.avaje/avaje-aws-appconfig).
## Step 2: Configure AWS AppConfig
### Basic Configuration (YAML)
Add AWS AppConfig settings to your `src/main/resources/application.yaml`:
```yaml
aws.appconfig:
enabled: true
application: my-application
environment: ${ENVIRONMENT:dev}
configuration: default
```
**Configuration Parameters:**
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `aws.appconfig.enabled` | No | `true` | Enable/disable AWS AppConfig plugin |
| `aws.appconfig.application` | Yes* | — | AWS AppConfig application name |
| `aws.appconfig.environment` | Yes* | — | AWS AppConfig environment name |
| `aws.appconfig.configuration` | No | `default` | Configuration profile name in AppConfig |
| `aws.appconfig.pollingEnabled` | No | `true` | Enable periodic polling for changes |
| `aws.appconfig.pollingSeconds` | No | `45` | Poll interval in seconds |
| `aws.appconfig.refreshSeconds` | No | `pollingSeconds - 1` | How long to cache between refreshes |
*Required only if `aws.appconfig.enabled` is `true`
### Advanced Configuration (YAML)
For more control, use:
```yaml
aws.appconfig:
enabled: true
application: my-application
environment: ${ENVIRONMENT:dev}
configuration: default
pollingEnabled: true
pollingSeconds: 60
refreshSeconds: 59
```
### Configuration with Properties Format
Alternatively, use `src/main/resources/application.properties`:
```properties
aws.appconfig.enabled=true
aws.appconfig.application=my-application
aws.appconfig.environment=${ENVIRONMENT:dev}
aws.appconfig.configuration=default
aws.appconfig.pollingEnabled=true
aws.appconfig.pollingSeconds=45
```
### Using Environment Variables
Use the `${VAR:defaultValue}` syntax to reference environment variables:
```yaml
aws.appconfig:
enabled: true
application: my-application
environment: ${ENVIRONMENT:dev}
configuration: ${CONFIG_PROFILE:default}
pollingSeconds: ${POLLING_INTERVAL:45}
```
## Step 3: Disable AWS AppConfig in Tests
Create `src/test/resources/application-test.yaml` to disable AWS AppConfig during testing:
```yaml
# Disable AWS AppConfig in test environment
aws.appconfig:
enabled: false
```
This prevents tests from requiring AWS credentials or network access to AWS AppConfig.
### Alternative with Properties Format
`src/test/resources/application-test.properties`:
```properties
aws.appconfig.enabled=false
```
## Step 4: Access Configuration from AWS AppConfig
AWS AppConfig configuration is automatically loaded and merged with your local configuration. Access it the same way as any other avaje-config properties:
```java
import io.avaje.config.Config;
public class AppService {
public void start() {
// AWS AppConfig values are accessed like any other properties
String featureName = Config.get("features.name");
int maxRetries = Config.getInt("service.max-retries", 3);
boolean featureEnabled = Config.getBool("features.enabled", false);
}
}
```
## How AWS AppConfig Loading Works
When your application starts:
1. **Local configuration** loads from `application.yaml` (local defaults)
2. **AWS AppConfig** plugin loads configuration from AWS AppConfig service
3. **AWS AppConfig values override local values** (if present)
4. **Polling starts** (if enabled) to check for configuration changes every `pollingSeconds`
5. **Configuration refreshes** at runtime when changes are detected
### Configuration Loading Order
```
1. src/main/resources/application.yaml (defaults)
2. AWS AppConfig (remote configuration - overrides defaults)
3. src/test/resources/application-test.yaml (test overrides)
4. System properties / command line arguments (final overrides)
```
## Dynamic Configuration Updates
### Polling for Changes
By default, AWS AppConfig polls for configuration changes every 45 seconds:
```yaml
aws.appconfig:
enabled: true
application: my-application
environment: production
pollingEnabled: true # Enable polling
pollingSeconds: 45 # Check every 45 seconds
refreshSeconds: 44 # Cache locally for 44 seconds
```
When changes are detected:
- Configuration values are updated
- Change listeners are notified (see next section)
- Your application can react to configuration changes
### Listening to Configuration Changes
Register listeners to react to configuration updates from AWS AppConfig:
```java
import io.avaje.config.Config;
public class FeatureManager {
public FeatureManager() {
// React to feature flag changes
Config.onChange("features.enabled", newValue -> {
System.out.println("Feature toggled to: " + newValue);
});
Config.onChangeInt("service.max-retries", newValue -> {
System.out.println("Max retries changed to: " + newValue);
});
Config.onChangeBool("debug.enabled", newValue -> {
System.out.println("Debug mode: " + newValue);
});
}
}
```
### Manual Configuration Refresh
Force an immediate configuration refresh:
```java
import io.avaje.config.Config;
import io.avaje.config.Configuration;
Configuration config = Config.asConfiguration();
config.refresh(); // Immediately fetch from AWS AppConfig
```
## AWS AppConfig Configuration Format
AWS AppConfig stores configuration in YAML or properties format. The plugin automatically detects the format.
### YAML Format in AWS AppConfig
```yaml
service:
name: PaymentService
max-retries: 3
timeout-seconds: 30
features:
new-checkout: true
advanced-analytics: false
database:
pool-size: 20
```
### Properties Format in AWS AppConfig
```properties
service.name=PaymentService
service.max-retries=3
service.timeout-seconds=30
features.new-checkout=true
features.advanced-analytics=false
database.pool-size=20
```
## Complete Example
### Full Application Setup
**pom.xml:**
```xml
io.avaje
avaje-config
5.1
io.avaje
avaje-aws-appconfig
1.0
```
**src/main/resources/application.yaml:**
```yaml
app:
name: MyService
version: 1.0
aws.appconfig:
application: my-service-config
environment: ${ENVIRONMENT:dev}
configuration: default
pollingSeconds: 45
features:
new-ui: false
analytics: true
```
**src/test/resources/application-test.yaml:**
```yaml
aws.appconfig:
enabled: false
```
**src/main/java/MyApplication.java:**
```java
import io.avaje.config.Config;
import io.avaje.config.Configuration;
public class MyApplication {
private static final Configuration config = Config.asConfiguration();
public static void main(String[] args) {
String appName = Config.get("app.name");
int port = Config.getInt("app.port", 8080);
System.out.println("Starting " + appName);
// Register listeners for dynamic updates
Config.onChangeBool("features.new-ui", enabled -> {
System.out.println("New UI feature: " + enabled);
});
startApplication();
}
private static void startApplication() {
boolean analyticsEnabled = Config.getBool("features.analytics", true);
System.out.println("Analytics enabled: " + analyticsEnabled);
}
}
```
## GraalVM Native Image Support
AWS AppConfig works in GraalVM native images. The avaje-config library includes native image metadata, so no additional configuration is required.
### Building a Native Image with AWS AppConfig
Build your native image normally:
```bash
mvn clean package -Pnative
```
The native image will:
- Include application configuration files automatically
- Load AWS AppConfig configuration at startup
- Poll for changes using the configured polling interval
### Setting Environment at Runtime
Pass the environment as a system property to the native executable:
```bash
./target/my-app -DENVIRONMENT=prod
```
## Common Patterns
### Feature Flags from AWS AppConfig
```java
public class FeatureFlags {
public static boolean isNewCheckoutEnabled() {
return Config.enabled("features.checkout.new", false);
}
public static boolean isAdvancedSearchEnabled() {
return Config.enabled("features.search.advanced", false);
}
}
// Usage in code
if (FeatureFlags.isNewCheckoutEnabled()) {
useNewCheckoutFlow();
} else {
useOldCheckoutFlow();
}
```
### Dynamic Service Configuration
```java
public class ServiceConfig {
private final Configuration config;
public ServiceConfig() {
this.config = Config.asConfiguration();
}
public int getMaxPoolSize() {
return config.getInt("service.pool-size", 10);
}
public int getTimeoutSeconds() {
return config.getInt("service.timeout-seconds", 30);
}
public void watchMaxPoolSize(IntConsumer listener) {
config.onChangeInt("service.pool-size", listener);
}
}
```
### Configuration Wrapper with Change Tracking
```java
public class DynamicConfig {
private volatile int maxRetries;
public DynamicConfig() {
this.maxRetries = Config.getInt("service.max-retries", 3);
// Update when AWS AppConfig changes
Config.onChangeInt("service.max-retries", newValue -> {
this.maxRetries = newValue;
onConfigChanged();
});
}
public int getMaxRetries() {
return maxRetries;
}
private void onConfigChanged() {
System.out.println("Configuration updated from AWS AppConfig");
}
}
```
## Troubleshooting
### AWS Credentials Not Found
**Error:** `Unable to connect to AWS AppConfig - no AWS credentials available`
**Solutions:**
- Ensure IAM role is attached to EC2 instance or ECS task
- Set `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` environment variables
- Configure AWS credentials in `~/.aws/credentials` (development only)
- Use IAM roles in AWS (recommended for production)
### AWS AppConfig Disabled in Production
**Problem:** AWS AppConfig is disabled but should be active
**Solution:** Verify `aws.appconfig.enabled` is not set to `false` in production configuration. Check that test configuration overrides don't apply to production:
```yaml
# src/main/resources/application.yaml - Production
aws.appconfig:
enabled: true
application: my-app
environment: prod
# src/test/resources/application-test.yaml - Tests only
aws.appconfig:
enabled: false
```
### Configuration Not Updating
**Problem:** Changes in AWS AppConfig don't appear in the application
**Solutions:**
- Verify `aws.appconfig.pollingEnabled` is `true`
- Check `aws.appconfig.pollingSeconds` - the default is 45 seconds
- Ensure the configuration profile exists in AWS AppConfig
- Manually trigger refresh with `configuration.refresh()`
- Check application logs for AWS AppConfig errors
### Performance Issues with Frequent Polling
**Problem:** Polling interval is too short and causes performance issues
**Solution:** Increase polling interval based on your needs:
```yaml
aws.appconfig:
pollingSeconds: 120 # Poll every 2 minutes instead of 45 seconds
refreshSeconds: 119
```
## Integration with Existing Setup
If you already have avaje-config configured:
1. Add `avaje-aws-appconfig` dependency
2. Add `aws.appconfig` configuration to `application.yaml`
3. AWS AppConfig values automatically override local defaults
4. Access values using existing `Config` API
5. No other code changes needed
AWS AppConfig is transparent to the rest of your application - it acts as an additional configuration source that's loaded after local files and before command-line overrides.
## Next Steps
- Set up AWS AppConfig application and configuration profiles in AWS Console
- Configure feature flags and service settings in AppConfig
- Test dynamic updates by modifying configuration in AWS Console
- Monitor polling and configuration refresh in application logs
- Review [avaje-config documentation](https://avaje.io/config/) for advanced features