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.
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().
public class Student{
#NotNull
private Course course= null;
#CustomValidation(enumCourse = course)
private String details = null;
}
}
How can i pass the course variable to CustomValidation annotation? Im getting an error saying that course must be an enum constant expression.
I have written a custom validation interface and validator too.
Annotation property must be constant at compile time.
You cannot use variable there.
The keyword here is cross fields validation.
You have two option:
Create annotation at class level. There you have access to all properties of class and validation should be done easy
Or create annotation at method level which return all necessary fields for validations.
#CustomAnnotations
Pair<Course, String> getCourseAndDetailForValidation() {
return Pair.of(course, details)
}
You can change return type to match your taste, it may be a List, an Array, wrapper objects...
It's specified by section 9.6.1 of the JLS. The annotation member types must be one of:
primitive
String
Class
an Enum
another Annotation
an array of any of the above
Course must be one of those types.
I have a simple class that has one of its properties as a String array. As per this document, using #Valid on an array, collection etc. will recursively validate each element of the array/collection.
#Valid
#Pattern(regexp="^[_ A-Za-z0-9]+$")
public String[] defaultAppAdminRoles;
the above annotation on the property generates the following exception:
Exception in thread "main" javax.validation.UnexpectedTypeException: No validator could be found for type java.lang.String[]. See: #Pattern at public java.lang.String[] com.hm.vigil.platform.ops.model.Application.defaultAppAdminRoles
at org.apache.bval.jsr303.AnnotationProcessor.checkOneType(AnnotationProcessor.java:326)
at org.apache.bval.jsr303.AnnotationProcessor.getConstraintValidator(AnnotationProcessor.java:301)
at org.apache.bval.jsr303.AnnotationProcessor.applyConstraint(AnnotationProcessor.java:241)
at org.apache.bval.jsr303.AnnotationProcessor.processAnnotation(AnnotationProcessor.java:149)
at org.apache.bval.jsr303.AnnotationProcessor.processAnnotations(AnnotationProcessor.java:90)
at org.apache.bval.jsr303.Jsr303MetaBeanFactory.processClass(Jsr303MetaBeanFactory.java:134)
at org.apache.bval.jsr303.Jsr303MetaBeanFactory.buildMetaBean(Jsr303MetaBeanFactory.java:95)
at org.apache.bval.MetaBeanBuilder.buildForClass(MetaBeanBuilder.java:131)
at org.apache.bval.MetaBeanManager.findForClass(MetaBeanManager.java:102)
at org.apache.bval.jsr303.ClassValidator.validate(ClassValidator.java:140)
at com.hm.vigil.platform.commons.AbstractValidatable.isValid(AbstractValidatable.java:33)
at com.hm.vigil.platform.ops.model.Application.main(Application.java:54)
I am using Apache BVal as validation provider.
The question, is the above method correct?
If it is not correct, what is the correct way to validate an array/collection with bean validation?
If it is correct, then is it some limitation of Apache BVal?
Another thing worth mentioning is the introduction of type annotation in Java 8 which lets you annotate parameterized type
private List<#MyPattern String> defaultAppAdminRoles;
It's not yet in the bean-validation standard (surely in next version) but already available in hibernate-validator 5.2.1. Blog entry here for further information.
By adding the #Valid annotation like you've done, the validation algorithm is applied on each element (validation of the element constraints).
In your case the String class has no constraints. The #Pattern constraint you've added is applied to the array and not on each element of it. Since #Pattern constraint cannot be applied on a array, you are getting an error message.
You can create a custom validation constraint for your array (see Hibernate docs for more info) or you can use a wrapper class like #Jordi Castilla mentioned.
First... i'm not sure... but #Pattern only accepts regex, right? Correct sintax is not:
#Pattern("^[_ A-Za-z0-9]+$") // delete 'regexp='
If this is not the problem you can create a wrapper class with validators in the attributes:
public class Role {
#Pattern(regexp="^[_ A-Za-z0-9]+$")
String adminRole;
//getters and setters
}
Then just need to mark the field #Valid in your existing object:
#Valid
Role[] defaultAppAdminRoles;
I have interface Resource and several classes implementing it, for example Audio, Video... Further, I have created custom annotation MyAnnotation with Class type param:
#MyAnnotation(type = Audio.class)
class Audio {
...
}
#MyAnnotation(type = Video.class)
class Video{
...
}
In some other place in code I have to use Interface Resource as a returned type:
public class Operations<T extends Resource> {
....
#OtherAnnotation(type = Audio.class (if audio), type = Video.class (if video) )
T getResource();
....
}
The question is how to appropriatelly annotate annotation #OtherAnnotation depending of what kind of Resource type will be returned ?
What you are asking is for dynamic values for annotation attributes.
However annotations can only be set at compile time which is the reason why their values can only be compile time constants. You may only read them at runtime.
There was a similar question in which someone tried to generate the annotation value , it's answer explains why there is no way to dynamically generate a value used in annotation in a bit more detail. In that question there was an attempt to use a final class variable generated with a static method.
There are annotation processors which offer a bit more flexibility by handling placeholders. However i don't think this fits your case, as you want the dynamic values at runtime.
This answer refers to spring's use of the expression language for the Value annotation in which the placeholder (#Value("#{systemProperties.dbName})") gets overrided with the data from one of the property sources defined ( example in spring boot )
In any case, you will have to rethink your architecture a bit.
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;
}
}