I tried the approach mentioned on How get property name in a validation message, but cannot apply it to the following situation.
#Data
public class CompanyRequest {
#PositiveOrZero(message = "validation.amount.positiveOrZero")
int quantity;
// code omitted for brevity
}
Here is my *.properties file having message:
validation.amount.positiveOrZero=The ${fieldName} value must be positive or zero
So, how can I use the property name in the validation message?
${fieldName} could not be retrieved easily inside the annotation without major modifications inside the core mechanism that makes the evaluation.
An easier way to achieve what you want, considering that for each field the name would be different, is the following.
Inside properties file put the property
validation.amount.positiveOrZero= value must be positive or zero
And then define the annotation in the code as
#PositiveOrZero(message = "The quantity {validation.amount.positiveOrZero}")
int quantity;
Edit:
It seems that the property should not be defined in a simple application.properties file but in another file named ValidationMessages.properties in the classpath.
As documented here
setValidationMessageSource(MessageSource messageSource) Specify a
custom Spring MessageSource for resolving validation messages, instead
of relying on JSR-303's default "ValidationMessages.properties" bundle
in the classpath.
Related
I'm attempting to change the default type value for kafka listener with property "spring.json.value.default.type=" using my own annotation in spring-kafka. Currently, it's possible to overwrite it with following values:
properties="spring.json.value.default.type=com.package.class" which is canonical name of class.
I've made an annotation that sets the following value:
#MyAnnotation(topic = Topics.BUILD_CONFIG_CREATED, defaultType = ConstantsClass.TYPE_HEADER + "prz.student.finger.kafkaBSC.MyObjectDTO")
Is there any way to avoid hard typing the class name?
I would like to implement the option to use the following code(just giving the class that was imported):
#MyAnnotation(topic = Topics.BUILD_CONFIG_CREATED, defaultType = MyObjectDTO.class)
The closest to I've got is adding in my annotation:
#AliasFor(annotation = KafkaListener.class, attribute = "properties")
String defaultType() default headerType()+dtoType().getCanonicalName().toString();
String headerType() default "spring.json.value.default.type=";
Unfortunately, the constraints regarding the compilation time values for class in annotation blocks me from implementing it. Is there any way to inject the cannonical name without hard typing it, or any other way to implement this?
The properties property can contain SpEL (see its Javadocs).
Something like #{#someBean.type.name}; where someBean is a bean with a method public Class<?> getType().
In application.properties:
comment.length=3000
Now I'd like to use this constant:
#Entity(name="clients_client")
public class Client {
#Column(length="${comment.length}")
private String comment;
}
When compiling, I get this error:
java: incompatible types: java.lang.String cannot be converted to int
This is very close to being a duplicate of How to import value from properties file and use it in annotation?, but I think there is a subtle difference between the questions.
You are trying to refer to a property in the #Column annotation by using ${comment.length}. What is really happening is that you try to assign the String "${comment.length}" to the length attribute of the annotation. This is of course not allowed, it expects an int.
Java, or Spring, can not "magically" replace ${propertyName} with a property. Spring, however, has its own way of injecting property values:
#Value("${value.from.file}")
private String valueFromFile;
Even if your entity was a Spring bean (for example annotated with #Component), and you injected the property with #Value, it cannot be used in the annotation. This is because values in annotations need to be constant, and is explained in more detail in the accepted answer to the near duplicate question.
Now I'd like to use this constant:
It simply is not a constant, it is determined at runtime.
In Spring 4, using the #Value annotation, what is the right way to specify a system property as a default if a specified property does not exists?
Whereas this works for the no-default case:
#Value("${myapp.temp}")
private String tempDirectory;
This doesn't work when I need a default:
#Value("#{myapp.temp ?: systemProperties.java.io.tmpdir}")
private String tempDirectory;
Nor does this:
#Value("#{myapp.temp ?: systemProperties(java.io.tmpdir)}")
private String tempDirectory;
Both of these give me an exception at the time Spring is trying to create the bean:
org.springframework.beans.factory.BeanCreationException: Error creating bean
with name 'configurationService': Invocation of init method failed;
nested exception is java.lang.NullPointerException
Can this be done?
I tried the following and it worked for me:
#Value("${myapp.temp:#{systemProperties['java.io.tmpdir']}}")
private String tempDirectory;
The missing parts for you I believe was not using ?: and needing the #{}. According to this answer:
${...} is the property placeholder syntax. It can only be used to dereference properties.
#{...} is SpEL syntax, which is far more capable and complex. It can also handle property placeholders, and a lot more besides.
So basically what is happening is we are telling Spring to first interpret myapp.temp as property placeholder syntax by using the ${} syntax. We then use : instead of ?: (which is called the Elvis operator) since the elvis operator only applies to Spring Expression Language expressions, not property placeholder syntax. The third part of our statement is #{systemProperties['java.io.tmpdir']} which is telling Spring to interpret the next expression as a Spring Expression and allows us to get system properties.
Try systemProperties['java.io.tmpdir'].
It's a map, so if the key has a dot in the name, you should use [..]
For me it only works with different property-names (being property.name.a a key with a value in my application.properties and property.name.b a Environment-Variable) like:
#Value("${property.name.a:${property.name.b}}")
The same names didn´t work for me like expected (loading the default, when the first property isn´t present), e.g.:
#Value("${property.name.a:${property.name.a}}")
The purpose of my code is to load some settings value from a *.properties file so that I later can use these values in some if-statements in my code. I want to load in some list-structure, but since that seems hard, an array will do. I have not really gotten that far, since I am stuck at the trivial matter of loading just a String from the properties file.
When I try to debug my code that is using some spring specific data. I get some interesting behaviour, pointing on the definition in the code right above the breakpoint gives me that the variable value is null.
#Value(value = "${ViewableReportFilter.allStates.verify}")
String verifyStringStates;
public ViewableReportFilter() {
viewStates = null;
log.debug("Read in properties for states: verify:" + verifyStringStates);
/*BREAKPOINT HERE*/
in my my.properties file:
ViewableReportFilter.allStates.verify=ONHOLD
And my config to use the properties-file:
<context:property-placeholder location="classpath:properties/my.properties" order="1" ignore-unresolvable="true" />
Spring can't set the fields of an object before that object is created. The first thing Spring does is use reflection to instantiate your class. It'll use either Class#newInstance() or use Constructor#newInstance() depending on the context. Only when the constructor has finished its work and returned can Spring, again using reflection, set the value of fields.
An alternative is to put a #Value annotated parameter in the constructor parameter list and set your field inside your constructor from the argument that's given to it by Spring.
public ViewableReportFilter(#Value String verify) {
this.verifyStringStates = verify;
...
Go through the Spring documentation for its IoC container. It explains all of this in much detail.
Updated the constructor, and added Autowire annotation. No changes in the properties file, no XML.
String arrayOfStrings;
#Autowired
public ViewableReportFilter(
#Value("${TMSViewableReportFilter.allStates.verify}") String[] verifyStringStates) {
arrayOfStrings = verifyStringStates;
public logViewableReportFilter() {
log.debug("Read in properties for states: verify:" + arrayOfString);
}
Try using this:
#Value(value = "${allStates.verify}")
And in your property my.properties:
allStates.verify=ONHOLD
I'm trying to cast the output of a value to an integer:
#Value("${api.orders.pingFrequency}")
private Integer pingFrequency;
The above throws the error
org.springframework.beans.TypeMismatchException:
Failed to convert value of type 'java.lang.String' to required type 'java.lang.Integer';
nested exception is java.lang.NumberFormatException:
For input string: "(java.lang.Integer)${api.orders.pingFrequency}"
I've also tried #Value("(java.lang.Integer)${api.orders.pingFrequency}")
Google doesn't appear to say much on the subject. I'd like to always be dealing with an integer instead of having to parse this value everywhere it's used.
Workaround
I realize a workaround may be to use a setter method to run the conversion for me, but if Spring can do it I'd rather learn something about Spring.
Assuming you have a properties file on your classpath that contains
api.orders.pingFrequency=4
I tried inside a #Controller
#Controller
public class MyController {
#Value("${api.orders.pingFrequency}")
private Integer pingFrequency;
...
}
With my servlet context containing :
<context:property-placeholder location="classpath:myprops.properties" />
It worked perfectly.
So either your property is not an integer type, you don't have the property placeholder configured correctly, or you are using the wrong property key.
I tried running with an invalid property value, 4123;. The exception I got is
java.lang.NumberFormatException: For input string: "4123;"
which makes me think the value of your property is
api.orders.pingFrequency=(java.lang.Integer)${api.orders.pingFrequency}
I was looking for the answer on internet and I found the following
#Value("#{new java.text.SimpleDateFormat('${aDateFormat}').parse('${aDateStr}')}")
Date myDate;
So in your case you could try with this
#Value("#{new Integer('${api.orders.pingFrequency}')}")
private Integer pingFrequency;
I had the exact same situation. It was caused by not having a PropertySourcesPlaceholderConfigurer in the Spring context, which resolves values against the #Value annotation inside of classes.
Include a property placeholder to solve the problem, no need to use Spring expressions for integers (the property file does not have to exist if you use ignore-resource-not-found="true"):
<context:property-placeholder location="/path/to/my/app.properties"
ignore-resource-not-found="true" />
If you want to convert a property to an integer from properties file there are 2 solutions which I found:
Given scenario: customer.properties contains customer.id = 100 as a field and you want to access it in spring configuration file as integer.The property customerId is declared as type int in the Bean Customer
Solution 1:
<property name="customerId" value="#{T(java.lang.Integer).parseInt('${customer.id}')}" />
In the above line, the string value from properties file is converted to int type.
solution 2: Use some other extension inplace of propeties.For Ex.- If your properties file name is
customer.properties then make it customer.details and in the configuration file use the below code
<property name="customerId" value="${customer.id}" />
If you are using #Configuation then instantiate below static bean. If not static #Configutation is instantiated very early and and the BeanPostProcessors responsible for resolving annotations like #Value, #Autowired etc, cannot act on it. Refer here
#Bean
public static PropertySourcesPlaceholderConfigurer propertyConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
I had the same issue I solved using this. Refer this Spring MVC: #Value annotation to get int value defined in *.properties file
#Value(#{propertyfileId.propertyName})
works
when use #Value, you should add #PropertySource annotation on Class, or specify properties holder in spring's xml file.
eg.
#Component
#PropertySource("classpath:config.properties")
public class BusinessClass{
#Value("${user.name}")
private String name;
#Value("${user.age}")
private int age;
#Value("${user.registed:false}")
private boolean registed;
}
config.properties
user.name=test
user.age=20
user.registed=true
this works!
Of course, you can use placeholder xml configuration instead of annotation.
spring.xml
<context:property-placeholder location="classpath:config.properties"/>
This problem also occurs when you have 2 resources with the same file name; say "configurations.properties" within 2 different jar or directory path configured within the classpath. For example:
You have your "configurations.properties" in your process or web application (jar, war or ear). But another dependency (jar) have the same file "configurations.properties" in the same path. Then I suppose that Spring have no idea (#_#?) where to get the property and just sends the property name declared within the #Value annotation.
In my case, the problem was that my POST request was sent to the same url as GET (with get parameters using "?..=..") and that parameters had the same name as form parameters. Probably Spring is merging them into an array and parsing was throwing error.
Since using the #Value("new Long("myconfig")") with cast could throw error on startup if the config is not found or if not in the same expected number format
We used the following approach and is working as expected with fail safe check.
#Configuration()
public class MyConfiguration {
Long DEFAULT_MAX_IDLE_TIMEOUT = 5l;
#Value("db.timeoutInString")
private String timeout;
public Long getTimout() {
final Long timoutVal = StringUtil.parseLong(timeout);
if (null == timoutVal) {
return DEFAULT_MAX_IDLE_TIMEOUT;
}
return timoutVal;
}
}