The elements were left unbound Converting field into a map - java

I am trying to serialize the tenants field into a map so I can access it by key
Example:
multiTenancyProperties.getTenants().get("009-sss");
application.yml
multi-tenancy:
tenants:
007:
property: "Dummy property 1"
db: "db2"
008/dom:
property: "Dummy"
db: "sql"
009-sss:
property: "Dummy"
db: "sql"
Model:
#Configuration
#ConfigurationProperties(prefix = "multi-tenancy")
public class MultiTenancyProperties {
private Map<String, TenantDetails> tenants;
public Map<String, TenantDetails> getTenants() {
return tenants;
}
}
public class TenantDetails {
private String property = "";
private String db = "";
// etc
..
Inject into a class to use:
#Autowired
private MultiTenancyProperties multiTenancyProperties;
Error:
Binding to target [Bindable#9249f818 type = java.util.List<com.multitenancy.configuration.MultiTenancyProperties$TenantInstance>, value = 'provided', annotations = array<Annotation>[[empty]]] failed:
Property: multi-tenancy.tenants[7].db
Value: db2
Origin: URL [file:config/application.yml] - 14:11
Reason: The elements [multi-tenancy.tenants[7].db,multi-tenancy.tenants[7].property] were left unbound.
Property: multi-tenancy.tenants[7].property
Value: Dummy property 1
Origin: URL [file:config/application.yml] - 13:17
Reason: The elements [multi-tenancy.tenants[7].db,multi-tenancy.tenants[7].property] were left unbound.
Action:
Update your application's configuration
SpringBoot v2.6.2

Related

Declaring beans inside a ConfigurationProperties class, using the same configuration property

I have a java class called UserData, and it has a field called users that I'm reading from application.yml in my spring-boot application, by using the #ConfigurationProperties annotation.
In this class I want to declare another bean to find the oldest user. When I try to create the getOldestUser bean, I'm getting a NullPointerException in the initialization of getOldestUser bean, because the users variable is null.
Here's how my code looks like:
#Data
#AllArgsConstructor
#NoArgsConstructor
#Component
#Configuration
#ConfigurationProperties
#EnableConfigurationProperties
public class UserData {
Map<String, Integer > users;
#Bean
public String getOldestUser() {
Integer maxAge = 0;
String oldestUser = null;
for (Map.Entry<String, Integer> entry : users.entrySet()) { // This line throws NPE, because 'users' is null
if(entry.getValue() > maxAge) {
oldestUser = entry.getKey();
maxAge = entry.getValue();
}
}
return oldestUser;
}
}
Here's the users declaration in application.yml:
users:
joey: 32
jim: 29
How can I access the fields of the class bean userData inside it's method bean getOldestUser? I've also tried passing the userData bean as an argument to this method, but that doesn't work either.
Firstly, try to split the configuration part from properties to separate classes.
Also, the #ConfigurationProperties annotation needs to have a prefix which binds all child properties to the fields.
Lastly, the method getOldestUser which you made as a spring bean is just a calculating function which can be under the UserProperties class and called whenever you need this data, spring is not required :).
data:
users:
joey: 32
jim: 29
#Getter
#Setter
#ConfigurationProperties(prefix = "data")
public class UserProperties{
private Map<String, Integer > users;
public String getOldestUser() {
Integer maxAge = 0;
String oldestUser = null;
for (Map.Entry<String, Integer> entry : users.entrySet()) { // This line throws NPE, because 'users' is null
if(entry.getValue() > maxAge) {
oldestUser = entry.getKey();
maxAge = entry.getValue();
}
}
return oldestUser;
}
}
#Configuration
#EnableConfigurationProperties(UserProperties.class)
public class UserConfiguration {}

How to pass Map<String,List<String>> in application.properties spring boot app

I have to configure below key value pair in my application.yml file where key will be a string and value will be List of
Eg: Map<String,List<String>> : How this we should configure in application.yml:
movement = accountId,positionId,accountName
transaction = transctionId,transctionName,transctionDate
Assuming you have the following attribute in your Configuration class:
private Map<String,List<String>> config;
You could achieve this with the following configuration in application.yml:
config:
movement:
- accountId
- positionId
- accountName
transaction:
- transctionId
- transctionName
- transctionDate
You can put this in your application.yml:
kv:
movement:
- accountId
- positionId
- accountName
transaction:
- transctionId
- transctionName
- transctionDate
Create a configuration class like this :
#Configuration
#ConfigurationProperties
public class TestConfig {
private Map<String,List<String>> kv;
public Map<String, List<String>> getKv() {
return kv;
}
public void setKv(Map<String, List<String>> kv) {
this.kv = kv;
}
}
Then inject TestConfig instance as
#Autowired
private TestConfig testConfig;
and retrive your key pair values like this :
Map<String,List<String>> kvs = testConfig.getKv();
List<String> listMovement = kvs.get("movement");
List<String> listTransaction = kvs.get("transaction");

create yml with keysValues

I have a .yml in my Springboot, I am trying create an array in my .yml with a class java (key/value), but I get error when I execute the app.
Yml:
profiles:
- type: user
url: www
- type: admin
url: http
Then I get datas in my implements using my class Profile:
#Value("${profiles}")
private List<Profile> profiles;
Finally my class:
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
public class Profile {
private String type;
private String url;
}
Error:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'api': Injection of autowired dependencies failed; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'profiles' in value "${profiles}"
I want read my yml for check values:
for (int i = 0; i < this.profiles.size(); i++) {
Profile pr = this.profiles.get(i);
System.out.println(pr.getType() + pr.getUrl());
}
Please I need help, I search in google but only get "Arrays" ['1','2','3'] o two Arrays ...
I need a structure the key/value
profiles: [
{
'key':'user',
'url': 'www'
},
{
'key':'admin',
'url': 'http'
}
]
Ahh !! My profiles can have 2,4, 1000 registros... that's why I pick it up List
#Value("${profiles}")
private List<Profile> profiles;

SpringBoot 2 the elements were left unbound

i have a file application.yml with my Spring Boot application which is not willing to run.
According to logs the reason of The elements [simulator.geo.b12,simulator.geo.b13,simulator.geo.b21,simulator.geo.c6,simulator.geo.host] were left unbound. This property however is set in application.yml and compiler is even returning it's value.
I'll really appreciate if someone could lend me a hand with that issue.
simulator:
geo:
host: http://localhost:8080/
b12: http://localhost:8080/geo/b12
b13: http://localhost:8080/geo/b13
b21: http://localhost:8080/geo/b21
c6: http://localhost:8080/geo/c6
and java class
#Getter
#Configuration
#ConfigurationProperties(prefix = "simulator",ignoreUnknownFields = false)
public class VendorSimulatorProperties {
#Value("${simulator.geo.host:http://localhost:8080/}")
private String initUrl;
#Value("${simulator.geo.b12}")
private String geoB12Url;
#Value("${simulator.geo.b13}")
private String geoB13Url;
#Value("${simulator.geo.b21}")
private String geoB21Url;
#Value("${simulator.geo.c6}")
private String geoC6Url;
}
when i start to run application, i got the error msg :
**************************
APPLICATION FAILED TO START
***************************
Description:
Binding to target [Bindable#1c140c7c type = com.mathartsys.dlc.thirdparty.vendor.config.VendorSimulatorProperties$$EnhancerBySpringCGLIB$$eb0a550b, value = 'provided', annotations = array<Annotation>[#org.springframework.boot.context.properties.ConfigurationProperties(prefix=simulator, value=simulator, ignoreUnknownFields=false, ignoreInvalidFields=false)]] failed:
Property: simulator.geo.b12
Value: http://localhost:8080/geo/b12
Origin: class path resource [config/application-dev.yml]:204:14
Reason: The elements [simulator.geo.b12,simulator.geo.b13,simulator.geo.b21,simulator.geo.c6,simulator.geo.host] were left unbound.
Property: simulator.geo.b13
Value: http://localhost:8080/geo/b13
Origin: class path resource [config/application-dev.yml]:205:14
Reason: The elements [simulator.geo.b12,simulator.geo.b13,simulator.geo.b21,simulator.geo.c6,simulator.geo.host] were left unbound.
Property: simulator.geo.b21
Value: http://localhost:8080/geo/b21
Origin: class path resource [config/application-dev.yml]:206:14
Reason: The elements [simulator.geo.b12,simulator.geo.b13,simulator.geo.b21,simulator.geo.c6,simulator.geo.host] were left unbound.
Property: simulator.geo.c6
Value: http://localhost:8080/geo/c6
Origin: class path resource [config/application-dev.yml]:207:13
Reason: The elements [simulator.geo.b12,simulator.geo.b13,simulator.geo.b21,simulator.geo.c6,simulator.geo.host] were left unbound.
Property: simulator.geo.host
Value: http://localhost:8080/
Origin: class path resource [config/application-dev.yml]:203:15
Reason: The elements [simulator.geo.b12,simulator.geo.b13,simulator.geo.b21,simulator.geo.c6,simulator.geo.host] were left unbound.
this problem had confuse me long time, i hope someone can give me some advice;
i used springboot 2.0
thanks
The problem is that you are using the #ConfigurationProperties in a wrong way. You use either #ConfigurationProperties or #Value but not both.
The solution either fix your class to be usable for #ConfigurationProperties or remove the #ConfigurationProperties annotation.
#Getter
#Setter
#Component
#ConfigurationProperties(prefix = "simulator.geo",ignoreUnknownFields = false)
public class VendorSimulatorProperties {
private String host = "http://localhost:8080/";
private String b12;
private String b13;
private String b21;
private String c6;
}
You need to fix the prefix it should be simulator.geo and your properties should be named after the keys in your file. You will also require setter next to the getter. However this will also need to change the rest of your configuration, to use the newly generated getters.
For you it is probably easier to remove #ConfigurationProperties as you weren't really using them in the first place.
I got the same issue, but in my case, it's because I added #AllArgsConstructor but did not add #NoArgsConstructor. As the result, Spring was unable to create the config object (using the default constructor) so it just skips processing the configs and print out the warning.
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor // need to add this if you have #AllArgsConstructor
public class ServerConfiguration {
private String host;
private int port;
}

Read a Map with Spring #ConfigurationProperties in test

Following a advice from Spring Boot integration tests doesn't read properties files I created the following code, with the intention of reading a map from properties in my JUnit test.
(I am using yml format, and using #ConfigurationProperties instead of #Value)
#RunWith(SpringJUnit4ClassRunner.class)
#TestPropertySource(locations="classpath:application-test.yml")
#ContextConfiguration(classes = {PropertiesTest.ConfigurationClass.class, PropertiesTest.ClassToTest.class})
public class PropertiesTest {
#Configuration
#EnableConfigurationProperties
static class ConfigurationClass {
}
#ConfigurationProperties
static class ClassToTest {
private String test;
private Map<String, Object> myMap = new HashMap<>();
public String getTest() {
return test;
}
public void setTest(String test) {
this.test = test;
}
public Map<String, Object> getMyMap() {
return myMap;
}
}
#Autowired
private ClassToTest config;
#Test
public void testStringConfig() {
Assert.assertEquals(config.test, "works!");
}
#Test
public void testMapConfig() {
Assert.assertEquals(config.myMap.size(), 1);
}
}
My test configuration (in application-test.yml):
test: works!
myMap:
aKey: aVal
aKey2: aVal2
Strangely, the String "works!" is successfully read from the config file, but the map is not populated.
What am I missing?
Note: adding a map setter causes the following exception:
Caused by: org.springframework.validation.BindException: org.springframework.boot.bind.RelaxedDataBinder$RelaxedBeanPropertyBindingResult: 1 errors
Field error in object 'target' on field 'myMap': rejected value []; codes [typeMismatch.target.myMap,typeMismatch.myMap,typeMismatch.java.util.Map,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [target.myMap,myMap]; arguments []; default message [myMap]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'java.util.Map' for property 'myMap'; nested exception is java.lang.IllegalStateException: Cannot convert value of type 'java.lang.String' to required type 'java.util.Map' for property 'myMap': no matching editors or conversion strategy found]
at org.springframework.boot.bind.PropertiesConfigurationFactory.checkForBindingErrors(PropertiesConfigurationFactory.java:359)
at org.springframework.boot.bind.PropertiesConfigurationFactory.doBindPropertiesToTarget(PropertiesConfigurationFactory.java:276)
at org.springframework.boot.bind.PropertiesConfigurationFactory.bindPropertiesToTarget(PropertiesConfigurationFactory.java:240)
at org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor.postProcessBeforeInitialization(ConfigurationPropertiesBindingPostProcessor.java:330)
... 42 more
After some wonderful time with a debugger,
I believe that this is a bug / missing feature in TestPropertySourceUtils.addPropertiesFilesToEnvironment():
try {
for (String location : locations) {
String resolvedLocation = environment.resolveRequiredPlaceholders(location);
Resource resource = resourceLoader.getResource(resolvedLocation);
environment.getPropertySources().addFirst(new ResourcePropertySource(resource));
}
}
ResourcePropertySource can only deal with .properties files and not .yml.
In regular app, YamlPropertySourceLoader registered and can deal with .yml.
As a note:
TestPropertySourceUtils.addPropertiesFilesToEnvironment() is called by:
org.springframework.test.context.support.DelegatingSmartContextLoader.prepareContext()
(inherited from AbstractContextLoader)
DelegatingSmartContextLoader is the default context loader you receive if no loader is specified in #ContextConfiguration.
(in fact #ContextConfiguration specifies an interface, but AbstractTestContextBootstrapper.resolveContextLoader() changes it to a concrete class)
To resolve the problem, I changed my configuration to application-test.properties
and used that file in my test.
test=works!
myMap.aKey: aVal
Another comment: the setter on the map is NOT needed:
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html#boot-features-external-config-loading-yaml
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 (or Set) and you
either need to provide a setter, or initialize it with a mutable
value, e.g. this will bind to the properties above

Categories