azure-appconfiguration-java
Azure App Configuration SDK for Java. Centralized application configuration management with key-value settings, feature flags, and snapshots. Triggers: "ConfigurationClient java", "app configuration java", "feature flag java", "configuration setting java", "azure config java".
Install
npx skills add https://github.com/microsoft/skills/tree/main/.github/plugins/azure-sdk-java/skills/azure-appconfiguration-java
claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install microsoft-skills@llmmart
git clone https://github.com/microsoft/skills.git
The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole microsoft/skills collection as a plugin from our marketplace. Git is the plain clone.
Skill manifest
Azure App Configuration SDK for Java
Client library for Azure App Configuration, a managed service for centralizing application configurations.
Installation
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-data-appconfiguration</artifactId>
<version>1.8.0</version>
</dependency>
Or use Azure SDK BOM:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-sdk-bom</artifactId>
<version>{bom_version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-data-appconfiguration</artifactId>
</dependency>
</dependencies>
Prerequisites
- Azure App Configuration store
- Connection string or Entra ID credentials
Environment Variables
AZURE_APPCONFIG_CONNECTION_STRING=Endpoint=https://<store>.azconfig.io;Id=<id>;Secret=<secret> # Alternative to Entra ID auth
AZURE_APPCONFIG_ENDPOINT=https://<store>.azconfig.io # Required for all auth methods
AZURE_TOKEN_CREDENTIALS=prod # Required only if DefaultAzureCredential is used in production
Client Creation
With Connection String
import com.azure.data.appconfiguration.ConfigurationClient;
import com.azure.data.appconfiguration.ConfigurationClientBuilder;
ConfigurationClient configClient = new ConfigurationClientBuilder()
.connectionString(System.getenv("AZURE_APPCONFIG_CONNECTION_STRING"))
.buildClient();
Async Client
import com.azure.data.appconfiguration.ConfigurationAsyncClient;
ConfigurationAsyncClient asyncClient = new ConfigurationClientBuilder()
.connectionString(connectionString)
.buildAsyncClient();
With Entra ID (Recommended)
import com.azure.core.credential.TokenCredential;
import com.azure.identity.AzureIdentityEnvVars;
import com.azure.identity.DefaultAzureCredentialBuilder;
import com.azure.identity.ManagedIdentityCredentialBuilder;
// Local dev: DefaultAzureCredential. Production: set AZURE_TOKEN_CREDENTIALS=prod or AZURE_TOKEN_CREDENTIALS=<specific_credential>
TokenCredential credential = new DefaultAzureCredentialBuilder()
.requireEnvVars(AzureIdentityEnvVars.AZURE_TOKEN_CREDENTIALS)
.build();
// Or use a specific credential directly in production:
// See https://learn.microsoft.com/java/api/overview/azure/identity-readme?view=azure-java-stable#credential-classes
// TokenCredential credential = new ManagedIdentityCredentialBuilder().build();
ConfigurationClient configClient = new ConfigurationClientBuilder()
.credential(credential)
.endpoint(System.getenv("AZURE_APPCONFIG_ENDPOINT"))
.buildClient();
Key Concepts
| Concept | Description |
|---|---|
| Configuration Setting | Key-value pair with optional label |
| Label | Dimension for separating settings (e.g., environments) |
| Feature Flag | Special setting for feature management |
| Secret Reference | Setting pointing to Key Vault secret |
| Snapshot | Point-in-time immutable view of settings |
Configuration Setting Operations
Create Setting (Add)
Creates only if setting doesn't exist:
import com.azure.data.appconfiguration.models.ConfigurationSetting;
ConfigurationSetting setting = configClient.addConfigurationSetting(
"app/database/connection",
"Production",
"Server=prod.db.com;Database=myapp"
);
Create or Update Setting (Set)
Creates or overwrites:
ConfigurationSetting setting = configClient.setConfigurationSetting(
"app/cache/enabled",
"Production",
"true"
);
Get Setting
ConfigurationSetting setting = configClient.getConfigurationSetting(
"app/database/connection",
"Production"
);
System.out.println("Value: " + setting.getValue());
System.out.println("Content-Type: " + setting.getContentType());
System.out.println("Last Modified: " + setting.getLastModified());
Conditional Get (If Changed)
import com.azure.core.http.rest.Response;
import com.azure.core.util.Context;
Response<ConfigurationSetting> response = configClient.getConfigurationSettingWithResponse(
setting, // Setting with ETag
null, // Accept datetime
true, // ifChanged - only fetch if modified
Context.NONE
);
if (response.getStatusCode() == 304) {
System.out.println("Setting not modified");
} else {
ConfigurationSetting updated = response.getValue();
}
Update Setting
ConfigurationSetting updated = configClient.setConfigurationSetting(
"app/cache/enabled",
"Production",
"false"
);
Conditional Update (If Unchanged)
// Only update if ETag matches (no concurrent modifications)
Response<ConfigurationSetting> response = configClient.setConfigurationSettingWithResponse(
setting, // Setting with current ETag
true, // ifUnchanged
Context.NONE
);
Delete Setting
ConfigurationSetting deleted = configClient.deleteConfigurationSetting(
"app/cache/enabled",
"Production"
);
Conditional Delete
Response<ConfigurationSetting> response = configClient.deleteConfigurationSettingWithResponse(
setting, // Setting with ETag
true, // ifUnchanged
Context.NONE
);
List and Filter Settings
List by Key Pattern
import com.azure.data.appconfiguration.models.SettingSelector;
import com.azure.core.http.rest.PagedIterable;
SettingSelector selector = new SettingSelector()
.setKeyFilter("app/*");
PagedIterable<ConfigurationSetting> settings = configClient.listConfigurationSettings(selector);
for (ConfigurationSetting s : settings) {
System.out.println(s.getKey() + " = " + s.getValue());
}
List by Label
SettingSelector selector = new SettingSelector()
.setKeyFilter("*")
.setLabelFilter("Production");
PagedIterable<ConfigurationSetting> settings = configClient.listConfigurationSettings(selector);
List by Multiple Keys
SettingSelector selector = new SettingSelector()
.setKeyFilter("app/database/*,app/cache/*");
PagedIterable<ConfigurationSetting> settings = configClient.listConfigurationSettings(selector);
List Revisions
SettingSelector selector = new SettingSelector()
.setKeyFilter("app/database/connection");
PagedIterable<ConfigurationSetting> revisions = configClient.listRevisions(selector);
for (ConfigurationSetting revision : revisions) {
System.out.println("Value: " + revision.getValue() + ", Modified: " + revision.getLastModified());
}
Feature Flags
Create Feature Flag
import com.azure.data.appconfiguration.models.FeatureFlagConfigurationSetting;
import com.azure.data.appconfiguration.models.FeatureFlagFilter;
import java.util.Arrays;
FeatureFlagFilter percentageFilter = new FeatureFlagFilter("Microsoft.Percentage")
.addParameter("Value", 50);
FeatureFlagConfigurationSetting featureFlag = new FeatureFlagConfigurationSetting("beta-feature", true)
.setDescription("Beta feature rollout")
.setClientFilters(Arrays.asList(percentageFilter));
FeatureFlagConfigurationSetting created = (FeatureFlagConfigurationSetting)
configClient.addConfigurationSetting(featureFlag);
Get Feature Flag
FeatureFlagConfigurationSetting flag = (FeatureFlagConfigurationSetting)
configClient.getConfigurationSetting(featureFlag);
System.out.println("Feature: " + flag.getFeatureId());
System.out.println("Enabled: " + flag.isEnabled());
System.out.println("Filters: " + flag.getClientFilters());
Update Feature Flag
featureFlag.setEnabled(false);
FeatureFlagConfigurationSetting updated = (FeatureFlagConfigurationSetting)
configClient.setConfigurationSetting(featureFlag);
Secret References
Create Secret Reference
import com.azure.data.appconfiguration.models.SecretReferenceConfigurationSetting;
SecretReferenceConfigurationSetting secretRef = new SecretReferenceConfigurationSetting(
"app/secrets/api-key",
"https://myvault.vault.azure.net/secrets/api-key"
);
SecretReferenceConfigurationSetting created = (SecretReferenceConfigurationSetting)
configClient.addConfigurationSetting(secretRef);
Get Secret Reference
SecretReferenceConfigurationSetting ref = (SecretReferenceConfigurationSetting)
configClient.getConfigurationSetting(secretRef);
System.out.println("Secret URI: " + ref.getSecretId());
Read-Only Settings
Set Read-Only
ConfigurationSetting readOnly = configClient.setReadOnly(
"app/critical/setting",
"Production",
true
);
Clear Read-Only
ConfigurationSetting writable = configClient.setReadOnly(
"app/critical/setting",
"Production",
false
);
Snapshots
Create Snapshot
import com.azure.data.appconfiguration.models.ConfigurationSnapshot;
import com.azure.data.appconfiguration.models.ConfigurationSettingsFilter;
import com.azure.core.util.polling.SyncPoller;
import com.azure.core.util.polling.PollOperationDetails;
List<ConfigurationSettingsFilter> filters = new ArrayList<>();
filters.add(new ConfigurationSettingsFilter("app/*"));
SyncPoller<PollOperationDetails, ConfigurationSnapshot> poller = configClient.beginCreateSnapshot(
"release-v1.0",
new ConfigurationSnapshot(filters),
Context.NONE
);
poller.setPollInterval(Duration.ofSeconds(10));
poller.waitForCompletion();
ConfigurationSnapshot snapshot = poller.getFinalResult();
System.out.println("Snapshot: " + snapshot.getName() + ", Status: " + snapshot.getStatus());
Get Snapshot
ConfigurationSnapshot snapshot = configClient.getSnapshot("release-v1.0");
System.out.println("Created: " + snapshot.getCreatedAt());
System.out.println("Items: " + snapshot.getItemCount());
List Settings in Snapshot
PagedIterable<ConfigurationSetting> settings =
configClient.listConfigurationSettingsForSnapshot("release-v1.0");
for (ConfigurationSetting setting : settings) {
System.out.println(setting.getKey() + " = " + setting.getValue());
}
Archive Snapshot
ConfigurationSnapshot archived = configClient.archiveSnapshot("release-v1.0");
System.out.println("Status: " + archived.getStatus()); // archived
Recover Snapshot
ConfigurationSnapshot recovered = configClient.recoverSnapshot("release-v1.0");
System.out.println("Status: " + recovered.getStatus()); // ready
List All Snapshots
import com.azure.data.appconfiguration.models.SnapshotSelector;
SnapshotSelector selector = new SnapshotSelector().setNameFilter("release-*");
PagedIterable<ConfigurationSnapshot> snapshots = configClient.listSnapshots(selector);
for (ConfigurationSnapshot snap : snapshots) {
System.out.println(snap.getName() + " - " + snap.getStatus());
}
Labels
List Labels
import com.azure.data.appconfiguration.models.SettingLabelSelector;
configClient.listLabels(new SettingLabelSelector().setNameFilter("*"))
.forEach(label -> System.out.println("Label: " + label.getName()));
Async Operations
ConfigurationAsyncClient asyncClient = new ConfigurationClientBuilder()
.connectionString(connectionString)
.buildAsyncClient();
// Async list with reactive streams
asyncClient.listConfigurationSettings(new SettingSelector().setLabelFilter("Production"))
.subscribe(
setting -> System.out.println(setting.getKey() + " = " + setting.getValue()),
error -> System.err.println("Error: " + error.getMessage()),
() -> System.out.println("Completed")
);
Error Handling
import com.azure.core.exception.HttpResponseException;
try {
configClient.getConfigurationSetting("nonexistent", null);
} catch (HttpResponseException e) {
if (e.getResponse().getStatusCode() == 404) {
System.err.println("Setting not found");
} else {
System.err.println("Error: " + e.getMessage());
}
}
Best Practices
- Use labels — Separate configurations by environment (Dev, Staging, Production)
- Use snapshots — Create immutable snapshots for releases
- Feature flags — Use for gradual rollouts and A/B testing
- Secret references — Store sensitive values in Key Vault
- Conditional requests — Use ETags for optimistic concurrency
- Read-only protection — Lock critical production settings
- Use Entra ID — Preferred over connection strings
- Async client — Use for high-throughput scenarios
Reference Links
Files (skills)
-
references
-
examples.md 25.2 KB
# Azure App Configuration Java SDK - Examples Comprehensive code examples for the Azure App Configuration SDK for Java. ## Table of Contents - [Maven Dependency](#maven-dependency) - [Client Creation](#client-creation) - [Configuration Settings CRUD](#configuration-settings-crud) - [List and Filter Settings](#list-and-filter-settings) - [Feature Flags](#feature-flags) - [Secret References](#secret-references) - [Read-Only Settings](#read-only-settings) - [Snapshots](#snapshots) - [Labels](#labels) - [Async Operations](#async-operations) - [Error Handling](#error-handling) - [Complete Application Example](#complete-application-example) ## Maven Dependency ```xml <dependency> <groupId>com.azure</groupId> <artifactId>azure-data-appconfiguration</artifactId> <version>1.8.0</version> </dependency> <!-- For DefaultAzureCredential --> <dependency> <groupId>com.azure</groupId> <artifactId>azure-identity</artifactId> <version>1.14.2</version> </dependency> ``` Or use Azure SDK BOM: ```xml <dependencyManagement> <dependencies> <dependency> <groupId>com.azure</groupId> <artifactId>azure-sdk-bom</artifactId> <version>1.2.28</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>com.azure</groupId> <artifactId>azure-data-appconfiguration</artifactId> </dependency> </dependencies> ``` ## Client Creation ### With Connection String ```java import com.azure.data.appconfiguration.ConfigurationClient; import com.azure.data.appconfiguration.ConfigurationClientBuilder; String connectionString = System.getenv("AZURE_APPCONFIG_CONNECTION_STRING"); ConfigurationClient client = new ConfigurationClientBuilder() .connectionString(connectionString) .buildClient(); ``` ### With Entra ID (Recommended) ```java import com.azure.identity.DefaultAzureCredentialBuilder; String endpoint = System.getenv("AZURE_APPCONFIG_ENDPOINT"); ConfigurationClient client = new ConfigurationClientBuilder() .credential(new DefaultAzureCredentialBuilder().build()) .endpoint(endpoint) .buildClient(); ``` ### Async Client ```java import com.azure.data.appconfiguration.ConfigurationAsyncClient; ConfigurationAsyncClient asyncClient = new ConfigurationClientBuilder() .credential(new DefaultAzureCredentialBuilder().build()) .endpoint(endpoint) .buildAsyncClient(); ``` ## Configuration Settings CRUD ### Create Setting (Add) Creates only if setting doesn't exist: ```java import com.azure.data.appconfiguration.models.ConfigurationSetting; // Add with key, label, and value ConfigurationSetting setting = client.addConfigurationSetting( "app/database/connection", // key "Production", // label (can be null) "Server=prod.db.com;Database=myapp" // value ); System.out.println("Setting created:"); System.out.println(" Key: " + setting.getKey()); System.out.println(" Label: " + setting.getLabel()); System.out.println(" Value: " + setting.getValue()); System.out.println(" ETag: " + setting.getETag()); ``` ### Create or Update Setting (Set) Creates or overwrites: ```java ConfigurationSetting setting = client.setConfigurationSetting( "app/cache/enabled", "Production", "true" ); System.out.println("Setting set: " + setting.getKey() + " = " + setting.getValue()); ``` ### Create with ConfigurationSetting Object ```java ConfigurationSetting newSetting = new ConfigurationSetting() .setKey("app/feature/timeout") .setLabel("Production") .setValue("30000") .setContentType("application/json"); ConfigurationSetting created = client.addConfigurationSetting(newSetting); ``` ### Get Setting ```java ConfigurationSetting setting = client.getConfigurationSetting( "app/database/connection", "Production" ); System.out.println("=== Setting Details ==="); System.out.println("Key: " + setting.getKey()); System.out.println("Label: " + setting.getLabel()); System.out.println("Value: " + setting.getValue()); System.out.println("Content-Type: " + setting.getContentType()); System.out.println("Last Modified: " + setting.getLastModified()); System.out.println("ETag: " + setting.getETag()); System.out.println("Read-Only: " + setting.isReadOnly()); ``` ### Conditional Get (If Changed) Only fetch if modified since last retrieval: ```java import com.azure.core.http.rest.Response; import com.azure.core.util.Context; // First, get the setting with its ETag ConfigurationSetting setting = client.getConfigurationSetting("app/cache/enabled", "Production"); // Later, check if it changed Response<ConfigurationSetting> response = client.getConfigurationSettingWithResponse( setting, // Setting with ETag null, // Accept datetime true, // ifChanged - only fetch if modified Context.NONE ); if (response.getStatusCode() == 304) { System.out.println("Setting not modified since last fetch"); } else { ConfigurationSetting updated = response.getValue(); System.out.println("Setting was modified: " + updated.getValue()); } ``` ### Update Setting ```java // Simple update ConfigurationSetting updated = client.setConfigurationSetting( "app/cache/enabled", "Production", "false" ); System.out.println("Updated value: " + updated.getValue()); ``` ### Conditional Update (If Unchanged) Only update if no concurrent modifications: ```java // Get current setting with ETag ConfigurationSetting current = client.getConfigurationSetting("app/timeout", "Production"); // Modify the value current.setValue("60000"); // Update only if ETag matches (optimistic concurrency) Response<ConfigurationSetting> response = client.setConfigurationSettingWithResponse( current, // Setting with current ETag true, // ifUnchanged - only update if ETag matches Context.NONE ); if (response.getStatusCode() == 200) { System.out.println("Update successful"); } else { System.out.println("Conflict - setting was modified by another process"); } ``` ### Delete Setting ```java ConfigurationSetting deleted = client.deleteConfigurationSetting( "app/cache/enabled", "Production" ); if (deleted != null) { System.out.println("Deleted setting: " + deleted.getKey()); } ``` ### Conditional Delete ```java Response<ConfigurationSetting> response = client.deleteConfigurationSettingWithResponse( setting, // Setting with ETag true, // ifUnchanged Context.NONE ); if (response.getStatusCode() == 200) { System.out.println("Setting deleted"); } ``` ## List and Filter Settings ### List All Settings ```java import com.azure.data.appconfiguration.models.SettingSelector; import com.azure.core.http.rest.PagedIterable; PagedIterable<ConfigurationSetting> settings = client.listConfigurationSettings( new SettingSelector() ); System.out.println("=== All Settings ==="); for (ConfigurationSetting s : settings) { System.out.printf("%s [%s] = %s%n", s.getKey(), s.getLabel(), s.getValue()); } ``` ### List by Key Pattern ```java SettingSelector selector = new SettingSelector() .setKeyFilter("app/*"); // Wildcard pattern PagedIterable<ConfigurationSetting> settings = client.listConfigurationSettings(selector); for (ConfigurationSetting s : settings) { System.out.println(s.getKey() + " = " + s.getValue()); } ``` ### List by Label ```java SettingSelector selector = new SettingSelector() .setKeyFilter("*") .setLabelFilter("Production"); PagedIterable<ConfigurationSetting> settings = client.listConfigurationSettings(selector); ``` ### List by Multiple Keys ```java SettingSelector selector = new SettingSelector() .setKeyFilter("app/database/*,app/cache/*"); // Comma-separated patterns PagedIterable<ConfigurationSetting> settings = client.listConfigurationSettings(selector); ``` ### List Null Labels Only ```java SettingSelector selector = new SettingSelector() .setKeyFilter("*") .setLabelFilter("\0"); // Null label filter PagedIterable<ConfigurationSetting> settings = client.listConfigurationSettings(selector); ``` ### Select Specific Fields ```java import com.azure.data.appconfiguration.models.SettingFields; import java.util.EnumSet; SettingSelector selector = new SettingSelector() .setKeyFilter("*") .setFields(EnumSet.of(SettingFields.KEY, SettingFields.VALUE, SettingFields.LAST_MODIFIED)); PagedIterable<ConfigurationSetting> settings = client.listConfigurationSettings(selector); ``` ### List Revisions View history of changes: ```java SettingSelector selector = new SettingSelector() .setKeyFilter("app/database/connection"); PagedIterable<ConfigurationSetting> revisions = client.listRevisions(selector); System.out.println("=== Setting Revisions ==="); for (ConfigurationSetting revision : revisions) { System.out.printf("Value: %s, Modified: %s%n", revision.getValue(), revision.getLastModified()); } ``` ## Feature Flags ### Create Feature Flag ```java import com.azure.data.appconfiguration.models.FeatureFlagConfigurationSetting; import com.azure.data.appconfiguration.models.FeatureFlagFilter; import java.util.Arrays; // Simple feature flag FeatureFlagConfigurationSetting simpleFlag = new FeatureFlagConfigurationSetting("dark-mode", true) .setDescription("Enable dark mode theme"); client.addConfigurationSetting(simpleFlag); // Feature flag with percentage filter FeatureFlagFilter percentageFilter = new FeatureFlagFilter("Microsoft.Percentage") .addParameter("Value", 50); // 50% rollout FeatureFlagConfigurationSetting betaFlag = new FeatureFlagConfigurationSetting("beta-feature", true) .setDescription("Beta feature with gradual rollout") .setClientFilters(Arrays.asList(percentageFilter)); FeatureFlagConfigurationSetting created = (FeatureFlagConfigurationSetting) client.addConfigurationSetting(betaFlag); System.out.println("Feature flag created: " + created.getFeatureId()); ``` ### Create Feature Flag with Targeting Filter ```java import java.util.HashMap; import java.util.Map; Map<String, Object> targetingParams = new HashMap<>(); targetingParams.put("Audience", Map.of( "Users", Arrays.asList("user1@example.com", "user2@example.com"), "Groups", Arrays.asList( Map.of("Name", "beta-testers", "RolloutPercentage", 100), Map.of("Name", "employees", "RolloutPercentage", 50) ), "DefaultRolloutPercentage", 10 )); FeatureFlagFilter targetingFilter = new FeatureFlagFilter("Microsoft.Targeting") .setParameters(targetingParams); FeatureFlagConfigurationSetting targetedFlag = new FeatureFlagConfigurationSetting("new-dashboard", true) .setDescription("New dashboard with targeted rollout") .setClientFilters(Arrays.asList(targetingFilter)); client.addConfigurationSetting(targetedFlag); ``` ### Get Feature Flag ```java FeatureFlagConfigurationSetting flag = (FeatureFlagConfigurationSetting) client.getConfigurationSetting( ".appconfig.featureflag/beta-feature", // Key format for feature flags null ); System.out.println("=== Feature Flag ==="); System.out.println("Feature ID: " + flag.getFeatureId()); System.out.println("Enabled: " + flag.isEnabled()); System.out.println("Description: " + flag.getDescription()); System.out.println("Filters: " + flag.getClientFilters()); ``` ### Update Feature Flag ```java FeatureFlagConfigurationSetting flag = (FeatureFlagConfigurationSetting) client.getConfigurationSetting(".appconfig.featureflag/beta-feature", null); // Disable the feature flag.setEnabled(false); FeatureFlagConfigurationSetting updated = (FeatureFlagConfigurationSetting) client.setConfigurationSetting(flag); System.out.println("Feature flag updated. Enabled: " + updated.isEnabled()); ``` ### List Feature Flags ```java SettingSelector selector = new SettingSelector() .setKeyFilter(".appconfig.featureflag/*"); PagedIterable<ConfigurationSetting> flags = client.listConfigurationSettings(selector); System.out.println("=== Feature Flags ==="); for (ConfigurationSetting setting : flags) { if (setting instanceof FeatureFlagConfigurationSetting) { FeatureFlagConfigurationSetting flag = (FeatureFlagConfigurationSetting) setting; System.out.printf("%s: %s%n", flag.getFeatureId(), flag.isEnabled() ? "ON" : "OFF"); } } ``` ## Secret References ### Create Secret Reference ```java import com.azure.data.appconfiguration.models.SecretReferenceConfigurationSetting; SecretReferenceConfigurationSetting secretRef = new SecretReferenceConfigurationSetting( "app/secrets/api-key", // Key "https://myvault.vault.azure.net/secrets/api-key" // Key Vault secret URI ); SecretReferenceConfigurationSetting created = (SecretReferenceConfigurationSetting) client.addConfigurationSetting(secretRef); System.out.println("Secret reference created: " + created.getKey()); System.out.println("Points to: " + created.getSecretId()); ``` ### Get Secret Reference ```java SecretReferenceConfigurationSetting ref = (SecretReferenceConfigurationSetting) client.getConfigurationSetting("app/secrets/api-key", null); System.out.println("Secret URI: " + ref.getSecretId()); // To get the actual secret value, use Key Vault SDK // SecretClient kvClient = new SecretClientBuilder()... // KeyVaultSecret secret = kvClient.getSecret("api-key"); ``` ### Update Secret Reference ```java SecretReferenceConfigurationSetting ref = (SecretReferenceConfigurationSetting) client.getConfigurationSetting("app/secrets/api-key", null); // Point to a different secret version ref.setSecretId("https://myvault.vault.azure.net/secrets/api-key/abc123"); client.setConfigurationSetting(ref); ``` ## Read-Only Settings ### Set Read-Only ```java ConfigurationSetting readOnly = client.setReadOnly( "app/critical/setting", "Production", true // isReadOnly ); System.out.println("Setting is now read-only: " + readOnly.isReadOnly()); ``` ### Clear Read-Only ```java ConfigurationSetting writable = client.setReadOnly( "app/critical/setting", "Production", false // make writable ); System.out.println("Setting is writable: " + !writable.isReadOnly()); ``` ## Snapshots ### Create Snapshot ```java import com.azure.data.appconfiguration.models.ConfigurationSnapshot; import com.azure.data.appconfiguration.models.ConfigurationSettingsFilter; import com.azure.core.util.polling.SyncPoller; import com.azure.core.util.polling.PollOperationDetails; import java.time.Duration; import java.util.ArrayList; import java.util.List; List<ConfigurationSettingsFilter> filters = new ArrayList<>(); filters.add(new ConfigurationSettingsFilter("app/*")); // Include all app/* keys SyncPoller<PollOperationDetails, ConfigurationSnapshot> poller = client.beginCreateSnapshot( "release-v1.0", new ConfigurationSnapshot(filters), Context.NONE ); poller.setPollInterval(Duration.ofSeconds(10)); poller.waitForCompletion(); ConfigurationSnapshot snapshot = poller.getFinalResult(); System.out.println("=== Snapshot Created ==="); System.out.println("Name: " + snapshot.getName()); System.out.println("Status: " + snapshot.getStatus()); System.out.println("Item Count: " + snapshot.getItemCount()); System.out.println("Size: " + snapshot.getSizeInBytes() + " bytes"); System.out.println("Created: " + snapshot.getCreatedAt()); ``` ### Get Snapshot ```java ConfigurationSnapshot snapshot = client.getSnapshot("release-v1.0"); System.out.println("Snapshot: " + snapshot.getName()); System.out.println("Status: " + snapshot.getStatus()); System.out.println("Items: " + snapshot.getItemCount()); System.out.println("Retention: " + snapshot.getRetentionPeriod()); ``` ### List Settings in Snapshot ```java PagedIterable<ConfigurationSetting> settings = client.listConfigurationSettingsForSnapshot("release-v1.0"); System.out.println("=== Settings in Snapshot ==="); for (ConfigurationSetting setting : settings) { System.out.printf("%s = %s%n", setting.getKey(), setting.getValue()); } ``` ### Archive Snapshot ```java ConfigurationSnapshot archived = client.archiveSnapshot("release-v1.0"); System.out.println("Status: " + archived.getStatus()); // archived ``` ### Recover Snapshot ```java ConfigurationSnapshot recovered = client.recoverSnapshot("release-v1.0"); System.out.println("Status: " + recovered.getStatus()); // ready ``` ### List All Snapshots ```java import com.azure.data.appconfiguration.models.SnapshotSelector; SnapshotSelector selector = new SnapshotSelector() .setNameFilter("release-*"); PagedIterable<ConfigurationSnapshot> snapshots = client.listSnapshots(selector); System.out.println("=== Snapshots ==="); for (ConfigurationSnapshot snap : snapshots) { System.out.printf("%s - %s (%d items)%n", snap.getName(), snap.getStatus(), snap.getItemCount()); } ``` ## Labels ### List Labels ```java import com.azure.data.appconfiguration.models.SettingLabelSelector; PagedIterable<SettingLabel> labels = client.listLabels( new SettingLabelSelector().setNameFilter("*") ); System.out.println("=== Labels ==="); for (SettingLabel label : labels) { System.out.println("Label: " + (label.getName() == null ? "(null)" : label.getName())); } ``` ## Async Operations ### Async List with Reactive Streams ```java ConfigurationAsyncClient asyncClient = new ConfigurationClientBuilder() .credential(new DefaultAzureCredentialBuilder().build()) .endpoint(endpoint) .buildAsyncClient(); asyncClient.listConfigurationSettings(new SettingSelector().setLabelFilter("Production")) .subscribe( setting -> System.out.println(setting.getKey() + " = " + setting.getValue()), error -> System.err.println("Error: " + error.getMessage()), () -> System.out.println("Completed listing") ); // Keep application running for async operations Thread.sleep(5000); ``` ### Async Get and Set ```java asyncClient.getConfigurationSetting("app/timeout", "Production") .flatMap(setting -> { setting.setValue("45000"); return asyncClient.setConfigurationSetting(setting); }) .subscribe( updated -> System.out.println("Updated: " + updated.getValue()), error -> System.err.println("Error: " + error.getMessage()) ); ``` ## Error Handling ```java import com.azure.core.exception.HttpResponseException; import com.azure.core.exception.ResourceNotFoundException; try { ConfigurationSetting setting = client.getConfigurationSetting("nonexistent", null); } catch (ResourceNotFoundException e) { System.err.println("Setting not found"); } catch (HttpResponseException e) { int statusCode = e.getResponse().getStatusCode(); switch (statusCode) { case 401: System.err.println("Unauthorized - check credentials"); break; case 403: System.err.println("Forbidden - check permissions"); break; case 409: System.err.println("Conflict - setting already exists or ETag mismatch"); break; case 412: System.err.println("Precondition failed - setting was modified"); break; case 429: System.err.println("Rate limited - retry with backoff"); break; default: System.err.println("Error: " + e.getMessage()); } } ``` ## Complete Application Example ```java import com.azure.data.appconfiguration.ConfigurationClient; import com.azure.data.appconfiguration.ConfigurationClientBuilder; import com.azure.data.appconfiguration.models.*; import com.azure.core.http.rest.PagedIterable; import com.azure.identity.DefaultAzureCredentialBuilder; import java.util.*; public class AppConfigurationManager { private final ConfigurationClient client; private final String environment; public AppConfigurationManager(String environment) { this.client = new ConfigurationClientBuilder() .endpoint(System.getenv("AZURE_APPCONFIG_ENDPOINT")) .credential(new DefaultAzureCredentialBuilder().build()) .buildClient(); this.environment = environment; } public String getSetting(String key) { try { ConfigurationSetting setting = client.getConfigurationSetting(key, environment); return setting.getValue(); } catch (Exception e) { // Try without label try { ConfigurationSetting setting = client.getConfigurationSetting(key, null); return setting.getValue(); } catch (Exception ex) { return null; } } } public void setSetting(String key, String value) { client.setConfigurationSetting(key, environment, value); } public boolean isFeatureEnabled(String featureId) { try { String key = ".appconfig.featureflag/" + featureId; ConfigurationSetting setting = client.getConfigurationSetting(key, environment); if (setting instanceof FeatureFlagConfigurationSetting) { return ((FeatureFlagConfigurationSetting) setting).isEnabled(); } // Try without label setting = client.getConfigurationSetting(key, null); if (setting instanceof FeatureFlagConfigurationSetting) { return ((FeatureFlagConfigurationSetting) setting).isEnabled(); } } catch (Exception e) { // Feature flag doesn't exist - default to disabled } return false; } public void setFeatureEnabled(String featureId, boolean enabled) { String key = ".appconfig.featureflag/" + featureId; try { FeatureFlagConfigurationSetting flag = (FeatureFlagConfigurationSetting) client.getConfigurationSetting(key, environment); flag.setEnabled(enabled); client.setConfigurationSetting(flag); } catch (Exception e) { // Create new feature flag FeatureFlagConfigurationSetting newFlag = new FeatureFlagConfigurationSetting(featureId, enabled); newFlag.setLabel(environment); client.addConfigurationSetting(newFlag); } } public Map<String, String> getAllSettings(String prefix) { Map<String, String> result = new HashMap<>(); SettingSelector selector = new SettingSelector() .setKeyFilter(prefix + "*") .setLabelFilter(environment); PagedIterable<ConfigurationSetting> settings = client.listConfigurationSettings(selector); for (ConfigurationSetting setting : settings) { if (!(setting instanceof FeatureFlagConfigurationSetting) && !(setting instanceof SecretReferenceConfigurationSetting)) { result.put(setting.getKey(), setting.getValue()); } } return result; } public void createSnapshot(String snapshotName, String keyPrefix) { List<ConfigurationSettingsFilter> filters = new ArrayList<>(); filters.add(new ConfigurationSettingsFilter(keyPrefix + "*") .setLabel(environment)); var poller = client.beginCreateSnapshot(snapshotName, new ConfigurationSnapshot(filters), null); poller.waitForCompletion(); ConfigurationSnapshot snapshot = poller.getFinalResult(); System.out.printf("Snapshot '%s' created with %d items%n", snapshot.getName(), snapshot.getItemCount()); } public static void main(String[] args) { AppConfigurationManager config = new AppConfigurationManager("Production"); // Set some configuration config.setSetting("app/database/timeout", "30000"); config.setSetting("app/cache/ttl", "3600"); // Get configuration String timeout = config.getSetting("app/database/timeout"); System.out.println("Database timeout: " + timeout); // Feature flags config.setFeatureEnabled("dark-mode", true); if (config.isFeatureEnabled("dark-mode")) { System.out.println("Dark mode is enabled"); } // Get all app settings Map<String, String> appSettings = config.getAllSettings("app/"); System.out.println("\n=== All App Settings ==="); appSettings.forEach((key, value) -> System.out.printf("%s = %s%n", key, value)); // Create a snapshot for release config.createSnapshot("release-" + System.currentTimeMillis(), "app/"); } } ``` ## Environment Variables ```bash AZURE_APPCONFIG_CONNECTION_STRING=Endpoint=https://<store>.azconfig.io;Id=<id>;Secret=<secret> AZURE_APPCONFIG_ENDPOINT=https://<store>.azconfig.io # For DefaultAzureCredential AZURE_CLIENT_ID=<service-principal-client-id> AZURE_CLIENT_SECRET=<service-principal-secret> AZURE_TENANT_ID=<tenant-id> ``` ## Best Practices 1. **Use labels** — Separate configurations by environment (Dev, Staging, Production) 2. **Use snapshots** — Create immutable snapshots for releases 3. **Feature flags** — Use for gradual rollouts and A/B testing 4. **Secret references** — Store sensitive values in Key Vault, not App Configuration 5. **Conditional requests** — Use ETags for optimistic concurrency 6. **Read-only protection** — Lock critical production settings 7. **Use Entra ID** — Preferred over connection strings for security 8. **Async client** — Use for high-throughput scenarios
-
-
SKILL.md 13.6 KB
--- name: azure-appconfiguration-java description: | Azure App Configuration SDK for Java. Centralized application configuration management with key-value settings, feature flags, and snapshots. Triggers: "ConfigurationClient java", "app configuration java", "feature flag java", "configuration setting java", "azure config java". license: MIT metadata: author: Microsoft version: "1.0.0" package: com.azure:azure-data-appconfiguration --- # Azure App Configuration SDK for Java Client library for Azure App Configuration, a managed service for centralizing application configurations. ## Installation ```xml <dependency> <groupId>com.azure</groupId> <artifactId>azure-data-appconfiguration</artifactId> <version>1.8.0</version> </dependency> ``` Or use Azure SDK BOM: ```xml <dependencyManagement> <dependencies> <dependency> <groupId>com.azure</groupId> <artifactId>azure-sdk-bom</artifactId> <version>{bom_version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>com.azure</groupId> <artifactId>azure-data-appconfiguration</artifactId> </dependency> </dependencies> ``` ## Prerequisites - Azure App Configuration store - Connection string or Entra ID credentials ## Environment Variables ```bash AZURE_APPCONFIG_CONNECTION_STRING=Endpoint=https://<store>.azconfig.io;Id=<id>;Secret=<secret> # Alternative to Entra ID auth AZURE_APPCONFIG_ENDPOINT=https://<store>.azconfig.io # Required for all auth methods AZURE_TOKEN_CREDENTIALS=prod # Required only if DefaultAzureCredential is used in production ``` ## Client Creation ### With Connection String ```java import com.azure.data.appconfiguration.ConfigurationClient; import com.azure.data.appconfiguration.ConfigurationClientBuilder; ConfigurationClient configClient = new ConfigurationClientBuilder() .connectionString(System.getenv("AZURE_APPCONFIG_CONNECTION_STRING")) .buildClient(); ``` ### Async Client ```java import com.azure.data.appconfiguration.ConfigurationAsyncClient; ConfigurationAsyncClient asyncClient = new ConfigurationClientBuilder() .connectionString(connectionString) .buildAsyncClient(); ``` ### With Entra ID (Recommended) ```java import com.azure.core.credential.TokenCredential; import com.azure.identity.AzureIdentityEnvVars; import com.azure.identity.DefaultAzureCredentialBuilder; import com.azure.identity.ManagedIdentityCredentialBuilder; // Local dev: DefaultAzureCredential. Production: set AZURE_TOKEN_CREDENTIALS=prod or AZURE_TOKEN_CREDENTIALS=<specific_credential> TokenCredential credential = new DefaultAzureCredentialBuilder() .requireEnvVars(AzureIdentityEnvVars.AZURE_TOKEN_CREDENTIALS) .build(); // Or use a specific credential directly in production: // See https://learn.microsoft.com/java/api/overview/azure/identity-readme?view=azure-java-stable#credential-classes // TokenCredential credential = new ManagedIdentityCredentialBuilder().build(); ConfigurationClient configClient = new ConfigurationClientBuilder() .credential(credential) .endpoint(System.getenv("AZURE_APPCONFIG_ENDPOINT")) .buildClient(); ``` ## Key Concepts | Concept | Description | |---------|-------------| | Configuration Setting | Key-value pair with optional label | | Label | Dimension for separating settings (e.g., environments) | | Feature Flag | Special setting for feature management | | Secret Reference | Setting pointing to Key Vault secret | | Snapshot | Point-in-time immutable view of settings | ## Configuration Setting Operations ### Create Setting (Add) Creates only if setting doesn't exist: ```java import com.azure.data.appconfiguration.models.ConfigurationSetting; ConfigurationSetting setting = configClient.addConfigurationSetting( "app/database/connection", "Production", "Server=prod.db.com;Database=myapp" ); ``` ### Create or Update Setting (Set) Creates or overwrites: ```java ConfigurationSetting setting = configClient.setConfigurationSetting( "app/cache/enabled", "Production", "true" ); ``` ### Get Setting ```java ConfigurationSetting setting = configClient.getConfigurationSetting( "app/database/connection", "Production" ); System.out.println("Value: " + setting.getValue()); System.out.println("Content-Type: " + setting.getContentType()); System.out.println("Last Modified: " + setting.getLastModified()); ``` ### Conditional Get (If Changed) ```java import com.azure.core.http.rest.Response; import com.azure.core.util.Context; Response<ConfigurationSetting> response = configClient.getConfigurationSettingWithResponse( setting, // Setting with ETag null, // Accept datetime true, // ifChanged - only fetch if modified Context.NONE ); if (response.getStatusCode() == 304) { System.out.println("Setting not modified"); } else { ConfigurationSetting updated = response.getValue(); } ``` ### Update Setting ```java ConfigurationSetting updated = configClient.setConfigurationSetting( "app/cache/enabled", "Production", "false" ); ``` ### Conditional Update (If Unchanged) ```java // Only update if ETag matches (no concurrent modifications) Response<ConfigurationSetting> response = configClient.setConfigurationSettingWithResponse( setting, // Setting with current ETag true, // ifUnchanged Context.NONE ); ``` ### Delete Setting ```java ConfigurationSetting deleted = configClient.deleteConfigurationSetting( "app/cache/enabled", "Production" ); ``` ### Conditional Delete ```java Response<ConfigurationSetting> response = configClient.deleteConfigurationSettingWithResponse( setting, // Setting with ETag true, // ifUnchanged Context.NONE ); ``` ## List and Filter Settings ### List by Key Pattern ```java import com.azure.data.appconfiguration.models.SettingSelector; import com.azure.core.http.rest.PagedIterable; SettingSelector selector = new SettingSelector() .setKeyFilter("app/*"); PagedIterable<ConfigurationSetting> settings = configClient.listConfigurationSettings(selector); for (ConfigurationSetting s : settings) { System.out.println(s.getKey() + " = " + s.getValue()); } ``` ### List by Label ```java SettingSelector selector = new SettingSelector() .setKeyFilter("*") .setLabelFilter("Production"); PagedIterable<ConfigurationSetting> settings = configClient.listConfigurationSettings(selector); ``` ### List by Multiple Keys ```java SettingSelector selector = new SettingSelector() .setKeyFilter("app/database/*,app/cache/*"); PagedIterable<ConfigurationSetting> settings = configClient.listConfigurationSettings(selector); ``` ### List Revisions ```java SettingSelector selector = new SettingSelector() .setKeyFilter("app/database/connection"); PagedIterable<ConfigurationSetting> revisions = configClient.listRevisions(selector); for (ConfigurationSetting revision : revisions) { System.out.println("Value: " + revision.getValue() + ", Modified: " + revision.getLastModified()); } ``` ## Feature Flags ### Create Feature Flag ```java import com.azure.data.appconfiguration.models.FeatureFlagConfigurationSetting; import com.azure.data.appconfiguration.models.FeatureFlagFilter; import java.util.Arrays; FeatureFlagFilter percentageFilter = new FeatureFlagFilter("Microsoft.Percentage") .addParameter("Value", 50); FeatureFlagConfigurationSetting featureFlag = new FeatureFlagConfigurationSetting("beta-feature", true) .setDescription("Beta feature rollout") .setClientFilters(Arrays.asList(percentageFilter)); FeatureFlagConfigurationSetting created = (FeatureFlagConfigurationSetting) configClient.addConfigurationSetting(featureFlag); ``` ### Get Feature Flag ```java FeatureFlagConfigurationSetting flag = (FeatureFlagConfigurationSetting) configClient.getConfigurationSetting(featureFlag); System.out.println("Feature: " + flag.getFeatureId()); System.out.println("Enabled: " + flag.isEnabled()); System.out.println("Filters: " + flag.getClientFilters()); ``` ### Update Feature Flag ```java featureFlag.setEnabled(false); FeatureFlagConfigurationSetting updated = (FeatureFlagConfigurationSetting) configClient.setConfigurationSetting(featureFlag); ``` ## Secret References ### Create Secret Reference ```java import com.azure.data.appconfiguration.models.SecretReferenceConfigurationSetting; SecretReferenceConfigurationSetting secretRef = new SecretReferenceConfigurationSetting( "app/secrets/api-key", "https://myvault.vault.azure.net/secrets/api-key" ); SecretReferenceConfigurationSetting created = (SecretReferenceConfigurationSetting) configClient.addConfigurationSetting(secretRef); ``` ### Get Secret Reference ```java SecretReferenceConfigurationSetting ref = (SecretReferenceConfigurationSetting) configClient.getConfigurationSetting(secretRef); System.out.println("Secret URI: " + ref.getSecretId()); ``` ## Read-Only Settings ### Set Read-Only ```java ConfigurationSetting readOnly = configClient.setReadOnly( "app/critical/setting", "Production", true ); ``` ### Clear Read-Only ```java ConfigurationSetting writable = configClient.setReadOnly( "app/critical/setting", "Production", false ); ``` ## Snapshots ### Create Snapshot ```java import com.azure.data.appconfiguration.models.ConfigurationSnapshot; import com.azure.data.appconfiguration.models.ConfigurationSettingsFilter; import com.azure.core.util.polling.SyncPoller; import com.azure.core.util.polling.PollOperationDetails; List<ConfigurationSettingsFilter> filters = new ArrayList<>(); filters.add(new ConfigurationSettingsFilter("app/*")); SyncPoller<PollOperationDetails, ConfigurationSnapshot> poller = configClient.beginCreateSnapshot( "release-v1.0", new ConfigurationSnapshot(filters), Context.NONE ); poller.setPollInterval(Duration.ofSeconds(10)); poller.waitForCompletion(); ConfigurationSnapshot snapshot = poller.getFinalResult(); System.out.println("Snapshot: " + snapshot.getName() + ", Status: " + snapshot.getStatus()); ``` ### Get Snapshot ```java ConfigurationSnapshot snapshot = configClient.getSnapshot("release-v1.0"); System.out.println("Created: " + snapshot.getCreatedAt()); System.out.println("Items: " + snapshot.getItemCount()); ``` ### List Settings in Snapshot ```java PagedIterable<ConfigurationSetting> settings = configClient.listConfigurationSettingsForSnapshot("release-v1.0"); for (ConfigurationSetting setting : settings) { System.out.println(setting.getKey() + " = " + setting.getValue()); } ``` ### Archive Snapshot ```java ConfigurationSnapshot archived = configClient.archiveSnapshot("release-v1.0"); System.out.println("Status: " + archived.getStatus()); // archived ``` ### Recover Snapshot ```java ConfigurationSnapshot recovered = configClient.recoverSnapshot("release-v1.0"); System.out.println("Status: " + recovered.getStatus()); // ready ``` ### List All Snapshots ```java import com.azure.data.appconfiguration.models.SnapshotSelector; SnapshotSelector selector = new SnapshotSelector().setNameFilter("release-*"); PagedIterable<ConfigurationSnapshot> snapshots = configClient.listSnapshots(selector); for (ConfigurationSnapshot snap : snapshots) { System.out.println(snap.getName() + " - " + snap.getStatus()); } ``` ## Labels ### List Labels ```java import com.azure.data.appconfiguration.models.SettingLabelSelector; configClient.listLabels(new SettingLabelSelector().setNameFilter("*")) .forEach(label -> System.out.println("Label: " + label.getName())); ``` ## Async Operations ```java ConfigurationAsyncClient asyncClient = new ConfigurationClientBuilder() .connectionString(connectionString) .buildAsyncClient(); // Async list with reactive streams asyncClient.listConfigurationSettings(new SettingSelector().setLabelFilter("Production")) .subscribe( setting -> System.out.println(setting.getKey() + " = " + setting.getValue()), error -> System.err.println("Error: " + error.getMessage()), () -> System.out.println("Completed") ); ``` ## Error Handling ```java import com.azure.core.exception.HttpResponseException; try { configClient.getConfigurationSetting("nonexistent", null); } catch (HttpResponseException e) { if (e.getResponse().getStatusCode() == 404) { System.err.println("Setting not found"); } else { System.err.println("Error: " + e.getMessage()); } } ``` ## Best Practices 1. **Use labels** — Separate configurations by environment (Dev, Staging, Production) 2. **Use snapshots** — Create immutable snapshots for releases 3. **Feature flags** — Use for gradual rollouts and A/B testing 4. **Secret references** — Store sensitive values in Key Vault 5. **Conditional requests** — Use ETags for optimistic concurrency 6. **Read-only protection** — Lock critical production settings 7. **Use Entra ID** — Preferred over connection strings 8. **Async client** — Use for high-throughput scenarios ## Reference Links | Resource | URL | |----------|-----| | Maven Package | https://central.sonatype.com/artifact/com.azure/azure-data-appconfiguration | | GitHub | https://github.com/Azure/azure-sdk-for-java/tree/main/sdk/appconfiguration/azure-data-appconfiguration | | API Documentation | https://aka.ms/java-docs | | Product Docs | https://learn.microsoft.com/azure/azure-app-configuration | | Samples | https://github.com/Azure/azure-sdk-for-java/tree/main/sdk/appconfiguration/azure-data-appconfiguration/src/samples | | Troubleshooting | https://github.com/Azure/azure-sdk-for-java/blob/main/sdk/appconfiguration/azure-data-appconfiguration/TROUBLESHOOTING.md |
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.