validate ConfigurationProperties mapping - spring

Is there a way to validate application.properties (or yml) if the properties match Java bean that it is mapped to via #ConfigurationProperties - so that if there is a typo in an attribute, exception will be thrown?
I tried using #Validated but it works only if every property has #NotNull annotation - but this is not exactly what I want to achieve... there may be some nullable properties in the config and I still want to "validate" them
I just spent 2 hours debugging an issue and I found out, the problem is that I misspelled an attribute name
e.g. application.yml
property1: 1
properrrrrty2: 2
#Configuration
#ConfigurationProperties
public class AppConfig {
private String property1;
private String property2; // <--- this property does not match due to typo in application.yml
}

A)
If you want to be sure that always a property exists then use #Validated with #NotNull for that property. #NotNull will complain if it does not find that property. You can still have the property there with an empty value if that is what you mean with nullable properties and NotNull will not complain.
You can't say I want it to be able to be nullable but also the validator should complain when that property is null.
So to sum things up.
#NotEmpty property must exist and also not have an empty value
#NotNull property must just exist. It does not care if it exists with an empty value.
That's why I insist you go with NotNull for your requirements.
B)
Also I can think of another way to handle that.
#Component
public class AppConfig {
#Value("${property1}")
private String property1;
#Value("${property2}")
private String property2;
}
Using injection with #Value, spring will fail to initialize the singleton AppConfig during application startup if some property with exactly the same name does not exist on properties file, therefore you will be informed that no property with that name exists and the application will not start up.

You can specify ignoreUnknownFields = false to ensure that no unknown properties are defined under the corresponding prefix. (docs):
Flag to indicate that when binding to this object unknown fields should be ignored. An unknown field could be a sign of a mistake in the Properties.
Borrowing from your example:
#Configuration
#ConfigurationProperties(prefix = "myapp", ignoreUnknownFields = false)
public class AppConfig {
private String property1;
private String property2;
}
This means myapp.property1 and myapp.property2 are allowed but not required to be set, so they remain nullable.
Any other set property with the myapp prefix (such as myapp.properrrrrty2=2) will cause a startup failure and the offending property name will be logged in the exception.

Related

Hi I'm trying to add #Value("${name}") private String name; in dto field but not able to read it from application.properties

class dto{#Value("${name}") private String name; }
application.properties
op.name=${OP_NAME:22-2}
//When I try to read it is returning null how to solve this
The #Value annotation will only be processed on Spring-managed beans (e.g. a #Component annotated class).
Additionally, you would need to specify the property key in #Value as follows in order to match with the key defined in your property file:
#Value("${op.name:fallback}")
private String name;
Your properties file can then carry the configurable value:
op.name=Some Name

Throw error when properties marked with #JsonIgnore are passed

I have a requirement to mark certain properties in my REST beans as ignored using #JsonIgnore. (I am using Spring Boot). This helps in avoiding these properties in my Swagger REST documentation.
I also would like to ensure that if the client passes these properties, an error is sent back. I tried setting spring.jackson.deserialization.fail-on-unknown-properties=true, but that works only for properties that are truly unknown. The properties marked with #JsonIgnore passes through this check.
Is there any way to achieve this?
I think I found a solution -
If I add #JsonProperty(access = Access.READ_ONLY) to the field that is marked as #JsonIgnore, I get back a validation error. (I have also marked the property with #Null annotation. Here is the complete solution:
#JsonInclude(JsonInclude.Include.NON_NULL)
public class Employee {
#Null(message = "Id must not be passed in request")
private String id;
private String name;
//getters and setters
}
#JsonInclude(JsonInclude.Include.NON_NULL)
public class EmployeeRequest extends Employee {
#Override
#JsonIgnore
#JsonProperty(access = Access.READ_ONLY)
public void setId(String id) {
super.setId(id);
}
}
PS: By adding #JsonProperty(access = Access.READ_ONLY), the property started showing up in Swagger model I had to add #ApiModelProperty(hidden = true) to hide it again.
The create method takes EmployeeRequest as input (deserialization), and the get method returns Employee as response (serialization). If I pass id in create request, with the above solution, it gives me back a ConstraintViolation.
PS PS: Bummer. None of these solutions worked end-to-end. I ended up creating separate request and response beans - with no hierarchical relationship between them.

Spring does not complain if a property is not set when using ConfigurationProperties

I have a bean that is configured via ConfigurationProperties:
#Component
#ConfigurationProperties(prefix = "mybean")
public class MyBean {
#NotEmpty
private String name;
// Getters, setters, ...
}
I configure the field values via application.yml but in "two levels". In the default application.yml I just set the value to the value of another property:
myBean.name: ${theValueOf.myBean.name}
In the profile specific YML file I have:
theValueOf.myBean.name: 'The desired value'
My expectation would be that if I forget to specify the property theValueOf.myBean.name then the application should fail at startup with the message that the placeholder 'theValueOf.myBean.name' could not be resolved. Instead, the field name is assigned the value (literally) ${theValueOf.myBean.name}.
If I annotate the name field with #Value("${myBean.name}") (and do not use ConfigurationProperties), and forget to define the property theValueOf.myBean.name, then the application fails at startup -- as expected.
My question is: How can I make Spring fail at startup with the message 'Could not resolve placeholder ...' when using ConfigurationProperties?
Simply mark your properties with JSR303 annotations, inside your #ConfigurationProperties.
#Component
#ConfigurationProperties(prefix = "mybean")
public class MyBean {
#NotEmpty
private String name;
}

#Value to retrieve #Query value

Requirement
I've been asked to retrieve the value of a query annotation from a custom property stored in the application.properties file.
Prievious attempt
I tried to use #Value without success
#Repository
public interface FooRepository
extends JpaRepository<Foo, Long> {
#Value("${db.queries.distance}")
String distanceQuery; // this raises an error
#Query(nativeQuery = true, value =distanceQuery)
...
Eclipse marks "distanceQuery" and states
The blank final field distanceQuery may not have been initialized
And force me to initialize the variable as follows
public static final Double distanceQuery = null;
Unfortunately it's not what I want
Question
Is there a workaround to fix this issue?
Thank you in advance!
Instead of defining in a properties file, you can define query in a constants java file and can get value as below
#Query(nativeQuery = true, value =Constants.DISTANCE_QUERY)

Load Spring configuration in Hibernate entity

I want to configure Hibernate to set maximum length for a VARCHAR field. This maximum length is defined in a configuration file, and this configuration file is loaded by a class ValidationConfiguration.
This is my entity:
#Entity
public class MyEntity{
#Autowired /*I know this is bad practice,
I just want to let readers know that this object is instantiated.*/
private ValidationConfiguration config;
#Column(length = config.getMaxLength()) /*This gives a "java: element value
must be a constant expression"*/
String description;
//Get and set
}
Is this possible? If not, are there any workarounds?
From your code, it is clearly visible, that You are just defining the ValidationConfiguration by private ValidationConfiguration config;
But You are not Instantiating the Object.
So, Instantiate the object like new ValidationConfiguration() and since you haven't shared the code of ValidationConfiguration , i am predicting that your method is getMaxLength() not static. If the problem persists, do share the ValidationConfiguration code.
If that is a bean, then you can autowire it simply and don't create new instantiation.

Resources