Description I am trying to bind the following configuration with my Component class -
platform:
service:
config:
guard:
hostname: fancy-host1.kiki.com
resources:
- name: bark
api-path: dog/alert/bark/{dog-id}
- name: bite
api-path: dog/alert/bite/{dog-id}
json-path: $..kill-mode
play:
hostname: fancy-host2.kiki.com
resources:
- name: lick
api-path: dog/chill/lick/{dog-id}
json-path: $..cute-mode
My Component class looks something like this-
@Component
@ConfigurationProperties(prefix = "platform.service")
public class DogConfig
{
@Getter
@Setter
public class Resource
{
private String name;
private String apiPath;
private String jsonPath;
}
@Getter
@Setter
public class APIConfig
{
private String hostname;
private List<Resource> resources = new ArrayList<>();
}
private Map<ServiceType, APIConfig> config = new LinkedHashMap<>();
public Map<ServiceType, APIConfig> getConfig()
{
return config;
}
public void setConfig(Map<ServiceType, APIConfig> config)
{
this.config = config;
}
}
In the above code, ServiceType is an enum having values GUARD and PLAY.
Issue Though my spring boot application is not throwing any error on initializing, but it's not binding my YAML to DogConfig class. I'm not sure about what exactly I'm missing out here.
My troubleshooting effort so far I'm relying on this spring doc, to externalize my configurations. I know that @ConfigurationProperties are type safe and have individually tested binding of Enums, Maps and POJOs. But having all the three at once is something I'm not able to achieve.
please add static in your inner class of Resource and APIConfig for example:
public static class Resource {
private String name;
private String apiPath;
private String jsonPath;
}
You can do this:
Create a POJO like this:
@Getter
@Setter
@ConfigurationProperties("platform.service")
public class DogProperties {
private Map<String, APIConfig> config;
}
In your DogConfig you can do this for get the propierties:
@Configuration
@EnableConfigurationProperties(DogProperties.class)
public class DogConfig {
@Autowire
private DogProperties properties
...
@Bean
@Qualifier("guardConfig")
public APIConfig guardConfig(){
return properties.get("guard");
}
}
If you look this example, the secrete is a Map property, you can parse with a map with the key guard or play with APIConfig.
EDITED:
You could bind the properties to the Constructor using the ConstructorBinding annotation (see https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-external-config-constructor-binding) like this to transform the string key to an enum key with the valueOf() method:
@Component
@ConfigurationProperties(prefix = "platform.service")
@ConstructorBinding
public class DogConfig {
DogConfig(Map<String, APIConfig> config) {
this.config = config.entrySet().stream().collect(
Collectors.toMap(
e -> ServiceType.valueOf(e.getKey()),
Map.Entry::getValue
)
);
}
...
or this, by additionally using Jackson-Databind ObjectMapper to transform the wired map with additional map value type into one with pojo value type:
@Component
@ConfigurationProperties(prefix = "platform.service")
@ConstructorBinding
public class DogConfig {
DogConfig(Map<ServiceType, Map<String,Object> > config) {
this.config = config.entrySet().stream().collect(
Collectors.toMap(
Map.Entry::getKey,
e -> (new ObjectMapper()).convertValue(e.getValue(),APIConfig.class)
)
);
}
...
Like OP already hinted, a combination of both Enum and Pojo in a map does not seem to work. You may have to transform either of the two possible structures into the desired one.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With