How accept both long and double in Spring #ConfigurationProperties annotation? - java

Here is my yaml file:
collie:
crontask:
command: echo ok
intervalInMinute: 0.5
Here is the code:
#Configuration
#ConfigurationProperties(prefix = "collie.crontask")
#Data
public class CronTaskConfig {
private String command;
private Long intervalInMinute;
public void setIntervalInMinute(String intervalInMinute) {
this.intervalInMinute = Long.parseLong(intervalInMinute);
}
}
But I got this error:
Failed to bind properties under 'collie.crontask' to com.xx.union.collie.worker.config.CronTaskConfig$$EnhancerBySpringCGLIB$$e4948fd7:
Property: collie.crontask.intervalinminute
Value: 0.5
Origin: class path resource [application.yaml]:45:23
Reason: For input string: "0.5"
Action:
Update your application's configuration`
and :
org.springframework.boot.context.properties.ConfigurationPropertiesBindException: Error creating bean with name 'cronTaskConfig': Could not bind properties to 'CronTaskConfig'
But when I set intervalInMinute: 1, it works.
It looks like the problem is the type of value.
Can anyone help me to solve this problem?

0.5 is not a Long, and can't be converted. Changing to a double should work.

The value 0.5 is an invalid long value. You could parse the double value first:
public void setIntervalInMinute(String intervalInMinute) {
this.intervalInMinute = (long) Double.parseDouble(intervalInMinute);
}

Related

Micronaut native image ignores properties

I am trying to build graalvm native image of my micronaut application. I see the werid issue that some of the properties from application.yaml are ignored, though when I run the app via
./gradlew run
all works fine.
Here is my yaml
micronaut:
application:
name: phonebook
caches:
phonebook:
charset: UTF-8
router:
static-resources:
swagger:
paths: classpath:META-INF/swagger
mapping: /swagger/**
swagger-ui:
paths: classpath:META-INF/swagger/views/swagger-ui
mapping: /doc/**
endpoints:
caches:
enabled: true
# sensitive: false
env:
enabled: true
# sensitive: false
fauna:
secret: '${FAUNA_KEY}'
endpoint: https://db.eu.fauna.com:443
Here is how I read the properties in the class
#Value("${fauna.secret}")
private String faunaKey;
#Value("${fauna.endpoint}")
private String faunaEndpoint;
What could cause an issue?
I know this question has been asked a while ago, but after having spent some hours figuring out the solution to a very similar problem, I thought I'd share the solution here.
I found that apparently the graal compiler throws any private fields away, thus throwing an error saying something like:
Caused by: java.lang.NoSuchFieldError: No field 'value1' found for type: com.example.ConferenceConfig
The solution that worked for me, was to annotate the classes in question with #ReflectiveAccess.
However other solutions also work; use constructor injection or configure src/main/graal/reflect.json
#Factory
public class ConferenceConfig {
private final String value1;
public ConferenceConfig(#Value("${my.value1}") final String value1) {
this.value1 = value1;
}
#Context
public ConferenceConfigBean confBean() {
return new ConferenceConfigBean(value1);
}
public record ConferenceConfigBean(String value) {
public String getValue() {
return value;
}
}
}
This also works for records:
#Singleton
public record ConferenceService(#Value("${my.value1}") String value1) {
private static final List<Conference> CONFERENCES = Arrays.asList(
new Conference("Greach"),
new Conference("GR8Conf EU"),
new Conference("Micronaut Summit"),
new Conference("Devoxx Belgium"),
new Conference("Oracle Code One"),
new Conference("CommitConf"),
new Conference("Codemotion Madrid")
);
public Conference randomConf() {
return CONFERENCES.get(new Random().nextInt(CONFERENCES.size()));
}
public String getV1() {
return value1;
}
}
Another

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

Converting configuration properties to enum values

I have a configuration file which contains this line:
login.mode=PASSWORD
and an enum
public enum LoginMode {
PASSWORD, NOT_PASSWORD, OTHER }
and a spring bean
<bean id="environment" class="a.b.c.Environment" init-method="init">
<property name="loginMode" value="${login.mode}"/>
</bean>
and of course a bean class
public class Environment {
private LoginMode loginMode;
public LoginMode getLoginMode() {
return loginMode;
}
public void setLoginMode(LoginMode loginMode) {
this.loginMode = loginMode;
}
}
How can i convert the property of the configuration file (which is a String) into the corresponding enum value of LoginMode?
EDIT: i know how to get the enum value of a string input, but the issue is another one:
If i try this:
public class Environment {
private LoginMode loginMode;
public LoginMode getLoginMode() {
return loginMode;
}
public void setLoginMode(String loginMode) {
this.loginMode = LoginMode.valueOf(loginMode);
}
}
spring is complaining about getter and setter not having the same input and output type.
Bean property 'loginMode' is not writable or has an invalid setter method. Does the parameter type of the setter match the return type of the getter?
Spring automatically converts input Strings to the corresponding valueOf of the desired enum.
You can do that by
LoginMode.valueOf("someString");
LoginMode.valueOf(valueOfProperty);
EDIT:
Try using converter
http://docs.spring.io/spring/docs/3.0.0.RC2/reference/html/ch05s05.html
http://forum.spring.io/forum/spring-projects/web/83191-custom-enum-string-converters
EDIT2:
also check this:
How assign bean's property an Enum value in Spring config file?

Categories