How to define a conditional property with a variable sub property name - spring

I have a configuration property class that contains a map. The map is filled with values from a yml file. the structure looks like this.
config:
variable_sub_key_a:
key_a: 1234
key_b: 1234
variable_sub_key_b:
...
The configuration class looks like:
#Configuration
public class Configuration{
private Map<String, KEY> config= new HashMap<>();
}
I would like that the configuration class is only initialized by spring when the config element exists in the yml file. My problem is that a part of the property name can vary. So i can't use #ConditionalOnProperty. I tried it with different contidional-annotations but haven't found out how i could define a conditional that checks only that the property starts with config.* . Any hints how i can do this?

Related

How to load List from YAML in spring boot 2.6 [duplicate]

I am trying to load an array of strings from the application.yml file. This is the config:
ignore:
filenames:
- .DS_Store
- .hg
This is the class fragment:
#Value("${ignore.filenames}")
private List<String> igonoredFileNames = new ArrayList<>();
There are other configurations in the same class that loads just fine. There are no tabs in my YAML file. Still, I get the following exception:
Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'ignore.filenames' in string value "${ignore.filenames}"
use comma separated values in application.yml
ignoreFilenames: .DS_Store, .hg
java code for access
#Value("${ignoreFilenames}")
String[] ignoreFilenames
It is working ;)
My guess is, that the #Value can not cope with "complex" types. You can go with a prop class like this:
#Component
#ConfigurationProperties('ignore')
class IgnoreSettings {
List<String> filenames
}
Please note: This code is Groovy - not Java - to keep the example short! See the comments for tips how to adopt.
See the complete example https://github.com/christoph-frick/so-springboot-yaml-string-list
From the spring boot docs https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html
YAML lists are represented as property keys with [index] dereferencers, for example this YAML:
my:
servers:
- dev.bar.com
- foo.bar.com
Would be transformed into these properties:
my.servers[0]=dev.bar.com
my.servers[1]=foo.bar.com
To bind to properties like that using the Spring DataBinder utilities (which is what #ConfigurationProperties does) you need to have a property in the target bean of type java.util.List and you either need to provide a setter, or initialize it with a mutable value, e.g. this will bind to the properties above. Here is what the question's code would look like.
#ConfigurationProperties(prefix="ignore")
public class Filenames {
private List<String> ignoredFilenames = new ArrayList<String>();
public List<String> getFilenames() {
return this.ignoredFilenames;
}
}
In addition to Ahmet's answer you can add line breaks to the coma separated string using > symbol.
application.yml:
ignoreFilenames: >
.DS_Store,
.hg
Java code:
#Value("${ignoreFilenames}")
String[] ignoreFilenames;
Well, the only thing I can make it work is like so:
servers: >
dev.example.com,
another.example.com
#Value("${servers}")
private String[] array;
And dont forget the #Configuration above your class....
Without the "," separation, no such luck...
Works too (boot 1.5.8 versie)
servers:
dev.example.com,
another.example.com
In my case, this was a syntax issue in the .yml file. I had:
#Value("${spring.kafka.bootstrap-servers}")
public List<String> BOOTSTRAP_SERVERS_LIST;
and the list in my .yml file:
bootstrap-servers:
- s1.company.com:9092
- s2.company.com:9092
- s3.company.com:9092
was not reading into the #Value-annotated field. When I changed the syntax in the .yml file to:
bootstrap-servers: >
s1.company.com:9092,
s2.company.com:9092,
s3.company.com:9092
it worked fine.
#Value("#{'${your.elements}'.split(',')}")
private Set<String> stringSet;
yml file:
your:
elements: element1, element2, element3
There is lot more you can play with spring spEL.
Ahmet's answer provides on how to assign the comma separated values to String array.
To use the above configuration in different classes you might need to create getters/setters for this..
But if you would like to load this configuration once and keep using this as a bean with Autowired annotation, here is the how I accomplished:
In ConfigProvider.java
#Bean (name = "ignoreFileNames")
#ConfigurationProperties ( prefix = "ignore.filenames" )
public List<String> ignoreFileNames(){
return new ArrayList<String>();
}
In outside classes:
#Autowired
#Qualifier("ignoreFileNames")
private List<String> ignoreFileNames;
you can use the same list everywhere else by autowiring.
Configuration in yaml file:
ignore:
filenames: >
.DS_Store
.hg
In spring component:
#Value("#{'${gnore.filenames}'.split(' ')}")
private List<String> igonoredFileNames;
This worked fine for me.
#Value("${your.elements}")
private String[] elements;
yml file:
your:
elements: element1, element2, element3

Spring Boot 2.x binding YAML list into class problems

So I have been trying to a list property from my YAML to a class that I made and somehow the list binding can only recognize comma-separated values from the YAML.
Given this class
#Getter
#Configuration
#ConfigurationProperties
#PropertySource("classpath:test.yml")
public class SomeProperty {
final List<String> someList = new ArrayList<>();
}
And this yaml file
someList:
- a
- b
If I autowire the SomeProperty somewhere and print the someList, it will return an empty list, but if I change the yaml file to this
someList: a,b
Then the binding works and returns an array list containing the values. What is the problem with the binding?
Just found out that PropertySource can only recognize *.properties files that is why it cannot recognize yaml lists.

Springboot linking Environment values from Properties files but not from YAML files

This is strange, and now I am bit confused whats wrong in my approach.
I am using 2 Spring-Boot framework and writing simple Microservice apps.
Project-1
Constants.java
public static final String TEST_HOST = "${test1.host}"
application.properties
test1.host=https://somewhere.com
I am able to read this value without any problem i.e. https://somewhere.com.
Project-2
Constants.java
public static final String TEST_HOST = "${test1.host}"
application.yaml
test1:
host: https://somewhere.com
In above case it is giving me the value as ${test1.host} I am expecting that expression should get execute, but its treating as constant with string value and not environment value.
Instead of doing this:
public static final String TEST_HOST = "${test1.host}"
Add the annotation #Value so that you can inject properties from the configuration file like the following snippet:
#Value("${test1.host}")
public String host;
The same thing for the second project.
Do not forget to decorate the class holding the config values with this annotation #Configuration.

Spring Custom Configuration not populating properties with underscore

I am trying to populate Custom class with properties. following is my Custom Properties class:
#Configuration
#ConfigurationProperties(prefix = "foo.bar")
public class CustomProperties{
private String PROPERTY_ONE;
private String propertyTwo;
//setters
//getters
}
and my properties in application.properties are:
foo.bar.PROPERTY_ONE=some text
foo.bar.PROPERTY_TWO=some other text
When I am trying to use value from CustomProperties this is what I gets:
customProperties.getPROPERTY_ONE() = null
customProperties.getPopertyTwo() = some other text
So I observed that if I have variable name with underscore(_) in it not populating the property value.
is there any way to get the value with variable having underscore?
Yes, it is 100% possible to get your configuration values.
It's all about the casing! Inside of CustomProperties simply name your first property propertyOne ... and refactor your getters/setters appropriately ... and you'll be good to go!
Spring's got the camel casing going on when translating the configuration fields to your Configuration classes/properties. So instead of matching the casing of your properties, follow the camel casing equivalent of the property name found in your configuration file.
Example: PROPERTY_ONE translates to propertyOne

How do #value annotations work in Spring?

I've never worked with Spring before, and I've run into a configuration object that looks somewhat like this
public class Config {
#Value("${app.module.config1}")
private String config1;
#Value("${app.module.config2}")
private String config2
...
public String getConfig1() {
return config1;
}
...
Can anyone explain what is happening here? I'm assuming this is some type of code injection, but I can't find where these values are coming from!
They allow you to direct inject a Value from a properties file (system or declared property) in the variable. Using the util:properties tag you can add something like this in your applicationContext.xml
<util:properties id="message" location="classpath:com/your/program/resources/message.properties" />
Pointing for a properties file named "message.properties" with some content:
application.hello.message = Hello World!
And then, in your java source file, inject a direct value from this properties file using the #Value annotation:
#Value("#{message['application.hello.message']}")
private String helloWorldMessage;
#Value("${app.module.config1}")
This is part of the spring expression language where the spring framework would look for app.module.config1 JVM property from System.getProperties() and injects the value of that property into config1 attribute in that class. Please see this reference for more details in Spring 3.0.x and this reference for the current docs.

Resources