In addition to service injection, the EDC project will support configuration injection ("CI") in future releases.
In an effort to improve the ease-of-use and to lower the barrier of entry for developers we will provide a feature to configure extensions with an annotation mechanism. Resolving configuration can result in convoluted and difficult to read code.
- default values: it should be possible to provide a default value for configuration fields
- optionality: configuration fields that are not required should be
null
in case there is no config value for them - type flexibility: at least
String
,Integer
,Long
,Double
andBoolean
must be supported - annotation-based: the configuration injection is triggered by annotations
Configuration values are resolved during runtime startup, more specifically during the dependency injection phase.
Technically they are another type of InjectionPoint
.
Extension classes can declare fields of type String
, Integer
, Long
, Double
or Boolean
and annotate them with
the @Setting
annotation:
public class SomeExtension implements ServiceExtension {
@Setting(key = "edc.iam.publickey.alias")
private String publicKeyAlias;
}
This would check if a config value edc.iam.publickey.alias
is present on the Config
object, and if so, assign its
value to the field. An injection error would be raised if no config value is found for edc.iam.publickey.alias
. This
can be avoided by declaring a default value:
public class SomeExtension implements ServiceExtension {
@Setting(key = "edc.iam.publickey.alias", defaultValue = "foobar")
private String publicKeyAlias;
@Setting(key = "edc.some.timeout", defaultValue = "60")
private Integer someTimeout; // default value gets converted to int
@Setting(key = "edc.some.fraction", defaultValue = "barbaz")
private Double someFraction; // runtime exception if the default value is used: "barbaz" cannot be converted to double
}
Note that default values are supplied as Strings, and an attempt is made to convert them to the appropriate type. An injection error is raised raised if the value cannot be converted to the desired type.
Alternatively, config values can be marked as optional:
public class SomeExtension implements ServiceExtension {
@Setting(key = "edc.iam.publickey.alias", required = false)
private String publicKeyAlias;
}
If no config value is found for edc.iam.publickey.alias
, then the field is null
.
Note that if a defaultValue
is supplied, the required
attribute becomes meaningless.
In addition to supplying simple config values it should be possible to have the injection mechanism construct a config object:
public class SomeExtension implements ServiceExtension {
@Configuration
private KeyConfig keyConfig;
}
@Settings
public record KeyConfig(@Setting(key = "edc.iam.publickey.alias") String alias,
@Setting(key = "edc.iam.key.algorithm") String algorithm) {
}
// alternatively:
@Settings
public class KeyConfig {
@Setting(key = "edc.iam.publickey.alias")
private String alias;
@Setting(key = "edc.iam.key.algorithm")
private String algorithm;
// MUST have a public default constructor!
}
These are Java POJOs that are annotated with the @Settings
annotation and contain the actual config values. The same
principle applies as before, but the @Setting
-annotated fields are compounded in a class or a record
. However,
some limitations apply:
- the field must be annotated with
@Configuration
and the class must be annotated with@Settings
- they can only be declared inside an extension class
- config record classes can only contain constructors where every parameter is annotated with
@Setting
@Configuration
-annotated fields are optional if and only if all@Setting
-annotated fields within them have therequired = false
attribute. This optionality is implicit and cannot be configured.@Configuration
-annotated fields cannot have default values because those would have to be compile-time constant. However, all config values within can have default values.- all
@Setting
-annotated fields of a@Configuration
object must be optional or resolvable, either from config or via a default value, otherwise the CI mechanism fails with an error - if config objects are normal classes, they must have a public default constructor. All other constructors are ignored by the CI mechanism
@Setting
-annotated fields must not befinal
- nested config objects are not supported
We already have a @Setting
annotation in the runtime-metamodel
component, which we should re-use for this. A new
attribute named key
is added to the @Setting
annotation. If present, it triggers the config injection mechanism.
Currently, the description is the default value()
attribute of the @Setting
annotation. This should eventually be
changed so that the key
becomes the default value, and description
is another named attribute.
By piggy-backing on the dependency injection mechanism, configuration injection errors are automatically reported in the same way as service injection errors. Reporting dependency errors will be improved in future development iterations.