-
Notifications
You must be signed in to change notification settings - Fork 13
Getting started
Add the following dependency to your project:
For maven build
<dependency>
<groupId>org.zalando</groupId>
<artifactId>baigan-config</artifactId>
<version>${baigan.version}</version>
</dependency>
For gradle build
implementation 'org.zalando:baigan-config:${baigan.version}'
Baigan configurations follow a specific schema and can be stored on any of the supported repositories.
Configurations are stored in its simplest form as key values. A configuration is a pair of a dot(.) separated key and a value objects in JSON format.
A configuration object should conform to the following JSON Schema:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "Configuration",
"description": "A baigan configuration object value.",
"type": "object",
"properties": {
"alias": {
"description": "The identifier for the configuration, same as its key.",
"type": "string"
},
"description": {
"description": "Summary of the configuration.",
"type": "string"
},
"defaultValue": {
"description": "Default configuration if none of the condition is satisfied.",
"type": {}
},
"conditions": {
"description": "List of conditions to check",
"type": "array",
"items": {
"type": "object",
"properties": {
"value": {
"description": "Configuration value if this condition evaluates to true.",
"type": {}
},
"conditionType": {
"description": "Type of condition to evaluate. This can be custom defined, with custom defined properties.",
"type": "object"
}
}
}
}
},
"required": ["defaultValue"]
}
This sample JSON defines a configuration for the key express.feature.enabled
with the value true when the country_code is 3, and a default value of false.
[
{
"alias": "express.feature.enabled",
"description": "Feature toggle",
"defaultValue": false,
"conditions": [
{
"value": true,
"conditionType": {
"onValue": "3",
"type": "Equals"
},
"paramName": "country_code"
}
]
}
]
Application.java
@ComponentScan(basePackageClasses = { BaiganSpringContext.class })
@ConfigurationServiceScan(basePackages = { "com.foo.configurations" })
public class Application {
@Bean
public ConfigurationRepository configurationRepository(RepositoryFactory factory) {
return factory.fileSystemConfigurationRepository()
.fileName("configs.json")
.build();
}
}
-
@ComponentScan(basePackageClasses = { BaiganSpringContext.class })
loadsBaiganSpringContext
, which, in turn, loads all the necessary Spring beans required by baigan. -
@ConfigurationServiceScan(basePackages = { "com.foo.configurations" })
scans the provided packages to check for baigan-config interfaces (explained below). Baigan uses these interfaces to parse simple JSON configurations into concrete types. In this example, baigan will scan the com.foo.configurations package to identify baigan-config interfaces. -
You may consider enriching the repository builder with the Object mapper, In some cases you may face deserialization issues (e.g.,
Cannot construct instance of
clazz(no Creators, like default constructor, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
).
@ComponentScan(basePackageClasses = { BaiganSpringContext.class })
@ConfigurationServiceScan(basePackages = { "com.foo.configurations" })
public class Application {
@Bean
public ConfigurationRepository configurationRepository(RepositoryFactory factory, ObjectMapper objectMapper) {
return factory.fileSystemConfigurationRepository()
.fileName("configs.json")
.objectMapper(objectMapper)
.build();
}
}
ExpressFeature.java
@BaiganConfig
public interface ExpressFeature {
Boolean enabled();
}
Baigan follows structured configurations, where JSON configurations are automatically parsed into a specific type. @BaiganConfig
annotation serves as a hint to Baigan to map simple JSON configurations to a specific type in the client's application. Configuration interfaces should adhere to specific conventions.
- The interface should be annotated with @BaiganConfig.
- The interface name and the interface method should match the key name of the JSON configuration. For example, to map a
express.feature.enabled
key, Baigan takes the interface nameExpressFeature
and the interface methodenabled
to map the value.
Note: Primitives are not supported as return types, as they cannot be null and therefore cannot express a missing configuration value.
Caution
Primitives are not supported as return types as they cannot be null and therefore cannot express a missing configuration value.
If you use Baigan with Kotlin, it means you need to use nullable primitive types, e.g. Int?
instead of Int
.
@Component
public class ExpressServiceImpl implements ExpressService {
@Inject
private ExpressFeature expressFeature;
@Override
public void sendShipment(final Shipment shipment) {
if (expressFeature.enabled()) {
final String serviceUrl = expressFeature.serviceUrl();
// Use the configuration
}
}
}
Caution
Due to the way bean access is managed, concurrent use of Baigan's proxies from multiple threads during the early stages of Spring context initialization can result in concurrency issues, including the potential for deadlock. To mitigate this risk, it is advisable to refrain from accessing Baigan's proxies until the Spring context has been initialized.