Multiple configuration tree to one class in spring boot - java

i have this in Application.yaml:
override:
email:
enabled: true
value: "test#mycompany.com"
phone:
enabled: true
value: "+420666666666"
How do I make a single class configuration with these values?
I tried this:
public class RecipientOverrideConfig {
#Configuration
#ConfigurationProperties("override.email")
#Data
public class EmailOverride{
Boolean enabled;
String value;
}
#Configuration
#ConfigurationProperties("override.phone")
#Data
public class SmsOverride{
Boolean enabled;
String value;
}
}
But is there a better way to do this?

I suggest making the whole class a ConfigurationProperties
#ConfigurationProperties("override")
public class RecipientOverrideProperties {
private OverrideConfig email;
private OverrideConfig phone;
public class OverrideConfig {
private Boolean enabled;
private String value;
}
// getters and setters were omitted for brevity
}
And then autowire that into your configuration:
#Configuration
public class RecipientOverrideConfig {
#Autowired // or even better, use constructor injection
private RecipientOverrideProperties overrideProperties;
}

Related

How to forbid Spring Boot injecting both fields of the same type with the same bean instance?

I have a class with two final fields of the same type, and I need to make second field injected with null if property props.enabled in application.yml is false. However, if it's false Spring Boot injects both fields with the same bean instance.
How to forbid Spring Boot injecting both fields of the same type with the same bean instance?
#AllArgsConstructor
public class MySettings {
private int val;
}
My configuration class
#Configuration
public class MySpringConfig {
#Bean
public MySettings settingsA() {
return new MySettings(1);
}
#Bean
#ConditionalOnProperty(prefix = "props", name = "enabled")
public MySettings settingsB() {
return new MySettings(2);
}
}
And this is my class
#Component
#RequiredArgsConstructor
public class MyClass {
private final MySettings settingsA; // MySettings(1)
private final MySettings settingsB; // also MySettings(1) but must be null if props.enabled=false
#Value("${props.enabled}")
private boolean enabled;
...
}
This is part of the real project, so I have very little space to deviate from
UPDATE
I came up with solution of constructor injection but the code starts to look ugly
#Component
public class MyClass {
private final MySettings settingsA;
private final MySettings settingsB;
private boolean enabled;
public MyClass(MySettings settingsA,
#Nullable #Qualifier("settingsB") MySettings settingsB,
#Value("${props.enabled}") boolean enabled) {
this.settingsA = settingsA;
this.settingsB = settingsB;
this.enabled = enabled;
}
...
You need a qualifier to tell Spring which bean to inject:
#Configuration
public class MySpringConfig {
#Bean(name = "SettingsA")
public MySettings settingsA() {
return new MySettings(1);
}
#Bean(name = "SettingsB")
#ConditionalOnProperty(prefix = "props", name = "enabled")
public MySettings settingsB() {
return new MySettings(2);
}
}
And now in MyClass:
#Component
public class MyClass {
private final MySettings settingsA;
private final MySettings settingsB;
#Value("${props.enabled}")
private boolean enabled;
public MyClass(#Qualifier("SettingsA") MySettings settingsA, #Qualifier("SettingsB") MySettings settingsB) {
this.settingsA = settingsA;
this.settingsB = settingsB;
}
...
}
However, since one of those Beans might not be available I believe you need to not include it in the constructor injection otherwise you will get an error. In that case you need to do the following:
#Component
public class MyClass {
#Qualifier("SettingsA")
#Autowired
private MySettings settingsA;
#Qualifier("SettingsB")
#Autowired(required = false)
private MySettings settingsB;
#Value("${props.enabled}")
private boolean enabled;
...
}

Spring ConfigurationProperties file - giving a default value

I have a ConfigurationProperties class looks something like this (stripped out to show what i need)
#ConfigurationProperties(prefix = "something")
#Configuration
#Data
public class SomethingProps {
private OtherProps otherProps;
}
#Data
public class OtherProps {
private boolean enabled = true;
}
When I provide this in my application.yml file everything works fine
something:
otherProps: true
But when I don't provide otherProps at all in the yml file then i get a null pointer exception when autowiring OtherProps in a constructor. My expectation is that it would default to true, i have tried annotating with #NotNull
Initialize OtherProps.
#ConfigurationProperties(prefix = "something")
#Configuration
#Data
public class SomethingProps {
private OtherProps otherProps=new OtherProps();
}
#Data
public class OtherProps {
private boolean enabled = true;
}
I would suggest you import your config class instead of creating an object.
For eg.
#ConfigurationProperties(prefix = "something")
#Configuration
#Import({OtherProps.class})
#Data
public class SomethingProps {
}
and your OtherProps class will be like this,
#Data
public class OtherProps {
private boolean enabled = true;
}
try the following
#Configuration
#EnableConfigurationProperties(value = { OtherProps.class })
#Data
public class SomethingProps {
}
and then
#Data
#ConfigurationProperties(prefix = "something")
public class OtherProps {
private boolean enabled = true;
}
actually you can use #Value annotation as alternative
#Value("x.y.z:default_value")
private boolean isEnabled;
I usually use the following approach:
#ConfigurationProperties
public class SomethingProperties {
private OtherProps otherProps = new OtherProps();
// getters/setters/lombok generated whatever
public static class OtherProps {
private boolean enabled = true;
// getters/setters/lombok generated whatever
}
}
#Configuration
#EnableConfigurationProperties(SomethingProps.class)
public class MyConfiguration {
#Bean
public SampleBean sampleBean(SomethingProps config) {
return new SampleBean(config.getOtherProps().isEnabled());
}
}

How map properties file using #Configuration properites prefix with dotted properties in Spring?

I have a group of properties as follow:
spring.kafka.producer.edwh.bootstrap-servers=localhost:9092
spring.kafka.producer.edwh.properties.enable.idempotence=true
spring.kafka.producer.edwh.retries=10
spring.kafka.producer.edwh.transaction-id-prefix=slv
spring.kafka.producer.edwh.value-serializer=org.springframework.kafka.support.serializer.JsonSerializer
spring.kafka.producer.edwh.properties.spring.json.add.type.headers=false
... And I want to map in a class like this by using #ConfigurationProperties(prefix = "spring.kafka.producer.edwh"):
#ConfigurationProperties(prefix = "spring.kafka.producer.edwh")
public class EdwhKafkaProducerConfig {
private String bootstrap_servers;
private String properties_enable_idempotence;
private int retries;
private String transaction_id_prefix;
private String value_serializer;
private boolean properties_spring_json_add_type_headers;
}
... How can I do?
The dotted properties denote separate objects. So if you have
mail.additionalHeaders.redelivery=true
mail.additionalHeaders.secure=true
mail.credentials.username=john
mail.credentials.password=password
Then your config class can look like this:
#ConfigurationProperties(prefix = "mail")
public class ConfigProperties {
private AdditionalHeaders additionalHeaders;
private Credentials credentials;
// getters setters
public class AdditionalHeaders {
private boolean redelivery;
private boolean secure;
// getters setters
}
public class Credentials {
private String username;
private String password;
// getters setters
}
}
Have a look here:
https://www.baeldung.com/configuration-properties-in-spring-boot
If you are using Spring Boot, then you need to annotate your main application class with, in your case:
#EnableConfigurationProperties(value = EdwhKafkaProducerConfig.class)
You may also need to define public accessor methods for your configuration properties.

Spring boot YAML Config not reading all values

I'm trying to setup and use a YAML as config file in my Spring Boot 1.5.1 project.
My YAML file looks like the following:
hue:
user: cdKjsOQIRY8hqweAasdmx-WMsn
ip: "http://192.168.1.69"
scenes:
sunstatus:
enabled: true
id: 93yv8JekmAneCU9
group: 1
disable:
enabled: true
id: 93yv8JekmAneCU9
group: 6
It works perfectly fine to read hue.getUser(). However, hue.getScenes() returns null for some reason. My Java code for the Hue Config looks like the following:
#Configuration
#ConfigurationProperties(prefix = "hue")
public class Hue {
private String user;
private String ip;
private Scenes scenes;
/*
* Getters and setters of course
*/
public class Scenes {
private Sunstatus sunstatus;
private Disable disable;
/*
* Getters and setters
*/
public class Sunstatus {
private boolean enabled;
private String id;
private String group;
/*
* Getters and setters
*/
}
public class Disable {
private boolean enabled;
private String id;
private String group;
/*
* Getters and setters
*/
}
}
}
I've tried as well to annotate the each class with prefix as well, both in the format of hue.scenes.sunstatus, scenes.sunstatus and just sunstatus as well.
Additionally I also tried to use the #Value annotation a bit without any luck.
It's the same results if I keep the data in application.yml or in an external file. Can always only reach getUser().
What am I doing wrong here?
I see you are using public non-inner classes for nested configuration, so you should add the #NestedConfigurationProperty instead:
public class Scenes {
#NestedConfigurationProperty
private Sunstatus sunstatus;
#NestedConfigurationProperty
private Disable disable;
Nested properties
You can use the #NestedConfigurationProperty annotation on a field to indicate that a regular (non-inner) class should be treated as if it were nested.
So either add the annotations (if you plan on using the classes elsewhere) or make them public static.
try this.
#Configuration
#ConfigurationProperties(prefix = "hue")
public class Hue {
private String user;
private String ip;
private Scenes scenes = new Scenes();
/*
* Getters and setters of course
*/
public class Scenes {
private Sunstatus sunstatus = new Sunstatus();
private Disable disable = new Disable();
/*
* Getters and setters
*/
public class Sunstatus {
private boolean enabled;
private String id;
private String group;
/*
* Getters and setters
*/
}
public class Disable {
private boolean enabled;
private String id;
private String group;
/*
* Getters and setters
*/
}
}
}

How to group properties with Spring Boot Configuration Properties

According to Spring Boot documentation, properties can be grouped and a property may appear in more than one group. But at the moment when we create a property class marked with #ConfigurationProperties(prefix="test1") the group name will be the prefix which is test1. Now if I have another property class for example with prefix as "test2" how can I say this latter one has a property from the group test1?
--- UPDATE ---
Added nested class but it's not working
#Configuration
#Profile({"wmx"})
#EnableConfigurationProperties
#ConfigurationProperties(prefix = "myapp.wmx", locations = {"classpath:application-wmx.properties", "classpath:myapp-env.properties"})
public class WmxProperties {
/**
* The WMX implementation to be loaded.
*/
#NotNull(message = "Must be configured.")
private ProfileEnum profile;
//#ConfigurationProperties(locations = "classpath:myapp-env.properties")
public static class Env {
/**
* Host name for WMX.
*/
private String host;
/**
* Port number for WMX.
*/
//#Pattern(regexp = "^[1-9]\\d*$", message = "Positive port number only.")
private Integer port;
/**
* Provider name.
*/
#NotBlank
private String providerName;
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public Integer getPort() {
return port;
}
public void setPort(Integer port) {
this.port = port;
}
public String getProviderName() {
return providerName;
}
public void setProviderName(String providerName) {
this.providerName = providerName;
}
}
public ProfileEnum getProfile() {
return profile;
}
public void setProfile(ProfileEnum profile) {
this.profile = profile;
}
}
The commented annotation #ConfigurationProperties on the inner class is done after failing my tests. Spring doesn't load those properties with or without the annotation unless they are in the same property file, in this case application-emx.properties. Why is that? I want to separate these properties
=== RESOLVED ====
I noticed that I had to add a field of type the nested class with getter/setter methods otherwise Spring won't load the properties in the nested class
You can compose them with help of inner classes:
Property file
test1.property1=...
test1.test2.property2=...
test1.test2.property3=...
Java/Spring mapping:
import javax.validation.constraints.NotNull;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
#Getter
#Setter
#Configuration
#EnableConfigurationProperties
#ConfigurationProperties(locations = "classpath:myapp.properties")
public class ApplicationProperties {
private String property1;
private Test2 test2;
#Getter
#Setter
#ConfigurationProperties(prefix = "test2")
public static class Test2 {
#NotNull
private String property2;
#NotNull
private String property3;
}
}
We had success with this approach, because java composition mimics structure of property file. Also properties are validatable, so you can fail fast if configuration is not right.
Downside of this approach is that properties are mutable.
If your properties file is getting too big, your application most probably has wider problems.
The annotation processor automatically considers inner classes as nested properties. Make sure you have getters and setters defined.
#ConfigurationProperties(prefix="server")
public class ServerProperties {
private String name;
private Host host;
// ... getter and setters !!!
public static class Host {
private String ip;
private int port;
// ... getter and setters !!!
}
}
The same effect can be achieved with non-inner class but you should use the #NestedConfigurationProperty annotation on a field to indicate that a regular (non-inner) class should be treated as if it were nested.
#ConfigurationProperties(prefix="server")
public class ServerProperties {
private String name;
#NestedConfigurationProperty
private Host host;
// ... getter and setters !!!
}
public class Host {
private String ip;
private int port;
// ... getter and setters
}

Categories