Bind ZonedDateTime to bean from application.properties - spring-boot

I want to bind the current date to a bean via application.properties. I tried using a spell expression but it didn't work.
application.yml
defaults:
date: #{T(java.time.ZonedDateTime).now()}
And I have a java bean with getters and setters.
#ConfigurationProperties(prefix = "defaults")
public class DefaultProperties {
private ZonedDateTime date;
// Getters & Setters
}
The trouble is that the expression is always parsing as a string . I am getting Reason: Failed to convert property value of type 'java.lang.String' to required type 'java.time.ZonedDateTime'
Is there any way of doing this?

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

validate ConfigurationProperties mapping

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.

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;
}

NPE from Jackson trying to serializing a field that does not exists?

Here is my simple bean
#Data
#NoArgsConstructor
#AllArgsConstructor
#Builder
public class Foo {
private String firstName;
private String lastName;
public String getFullName(){
return firstName + lastName;
}
}
when this object gets serialized in Spring-boot controller with Jackson,
I get the following error
j.l.NullPointerException: null
com.example.foobar.foo.getFullName(Foo.java:28)
s.r.NativeMethodAccessorImpl.invoke0(NativeMethodAccessorImpl.java)
s.r.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
s.r.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
j.l.reflect.Method.invoke(Method.java:498)
c.f.j.d.s.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:653)
c.f.j.d.s.s.BeanSerializerBase.serializeFields(BeanSerializerBase.java:690)..
28 common frames omitted\nWrapped by: c.f.j.d.JsonMappingException: (was java.lang.NullPointerException)
(through reference chain: com.example.foobar.foo[\"fullName\"])
c.f.j.d.JsonMappingException.wrapWithPath(JsonMappingException.java:379)
c.f.j.d.JsonMappingException.wrapWithPath(JsonMappingException.java:339)
c.f.j.d.s.s.StdSerializer.wrapAndThrow(StdSerializer.java:343)
c.f.j.d.s.s.BeanSerializerBase.serializeFields(BeanSerializerBase.java:698)
c.f.j.d.s.BeanSerializer.serialize(BeanSerializer.java:155)
c.f.j.d.s.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:292)
c.f.j.d.ObjectWriter$Prefetch.serialize(ObjectWriter.java:1419)
c.f.j.d.ObjectWriter.writeValue(ObjectWriter.java:940)
o.s.h.c.j.AbstractJackson2HttpMessageConverter.writeInternal(AbstractJackson2HttpMessageConverter.java:267)...
23 common frames omitted\nWrapped by: o.s.h.c.HttpMessageNotWritableException: Could not write content: (was java.lang.NullPointerException) (through reference chain: com.example.foobar.foo[\"fullName\"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: (was java.lang.NullPointerException) (through reference chain: com.example.foobar.foo[\"fullName\"])
o.s.h.c.j.AbstractJackson2HttpMessageConverter.writeInternal(AbstractJackson2HttpMessageConverter.java:274 ..."
Here is the requestBody that I sent
{"firstName": "foo",
"lastName: null
}
Is Jackson trying to serialize fullName property ? but I have no such field declared. is this expected behavior? I am unable to find documentation that supports this behavior.
Thanks
Your guess is right, Jackson is trying to find the field name by its getter method, which it cannot find. And hence NPE.
Different solutions
use #JsonIgnore on the getFullName method.
you can disable this feature by setting this property in spring boot application.properties file
spring.jackson.mapper.use-getters-as-setters=false
If you wish to do it with the java code (If not using spring boot), you can do it with a bean declaration like this
#Bean
public Jackson2ObjectMapperBuilder objectMapperBuilder() {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder.featuresToDisable(MapperFeature.USE_GETTERS_AS_SETTERS);
return builder;
}

spring bind date field of command object

I have a form that inserts/updates data. The command object (Bean) class has a Date field that is placed in the form as follows:
<form:hidden path="createdDate">
when I submit the form, the BindResult.hasErrors() is validated as true.
I think I need to bind the date object, but how is it done for Command object field?
The form bean code is as follows
#Entity
#Table(name = "Employee")
public class Employee {
#Id
#GeneratedValue
#Column(name="id")
private int id;
#Column(name="EmployeeName")
private String employeeName;
#Column(name="CreatedDate")
private Date createdDate;
//Setter and getter methods
}
Error:
[Field error in object 'employee' on field 'CreatedDate': rejected value [Mon Sep 17 20:35:26 IST 2012]; codes [typeMismatch.employee.CreatedDate,typeMismatch.CreatedDate,typeMismatch.java.util.Date,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [subject.CreatedDate,CreatedDate]; arguments []; default message [CreatedDate]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'java.util.Date' for property 'CreatedDate'; nested exception is java.lang.IllegalStateException: Cannot convert value of type [java.lang.String] to required type [java.util.Date] for property 'CreatedDate': no matching editors or conversion strategy found]]
Add this annotation to your date fields:
#Column(name="CreatedDate")
#DateTimeFormat(pattern="yyyy/MM/dd hh:mm:ss") //whatever format is appropriate to you..
private Date createdDate;
Ensure that you have joda time as a dependency and the library is present in classpath. It will automatically register a converter to take care of the transformation.
I found your problem. In your Employee model class the createdDate field is not defined correctly.
You need to use the #Temporal annotation to define that the field is of type date.
Please put the following annotation also on top of the field declaration of createdDate
#Temporal(TemporalType.TIMESTAMP)
I think this should solve your problem. Cheers.

Resources