Is Default serialization format of Date has changed with recent Spring boot versions/Jackson Versions? - spring-boot

I am in the process of updating the SpringBoot version to 2.3.1.RELEASE from 2.2.2.RELEASE. Suddenly all date format of all my API responses has changed(In the timezone representation section).
From +0000 to +00:00
"timestamp": "2020-05-19T05:46:49.469+0000" - 2.2.2.RELEASE
"timestamp": "2020-06-30T09:55:23.014+00:00" - 2.3.1.RELEASE
This is my Simple POJO: I have not added any #JsonFortmat configuration for the date field.
#NoArgsConstructor
#AllArgsConstructor
#Getter
public class BuildInfo{
private String message;
private Date timestamp;
}
and my controller method
#GetMapping(value = "/buildinfo", produces = "application/json")
ResponseEntity<BuildInfo> getBuildInfo();
I have not added any serialization/ deserialization logic and I have developed only with SpringBoot's default behavior. I am trying to search any release document regarding this change, but could not find any.
Below changes, I can perform to adapt these changes
Using #JsonFormat annotation with customized pattern in every date field(but I cannot do it as this is client library)
#JsonFormat(pattern="yyyy-MM-dd'T'HH:mm:ss.SSSZ")
private Date timestamp;
Injecting Jackson2ObjectMapperBuilderCustomizer in application level.
#Bean
public Jackson2ObjectMapperBuilderCustomizer jsonCustomizer() {
return builder -> builder.simpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
}
But I would like to narrow down the root cause for the format change? starting from which version of Jackson/Spring libraries?

It comes from jackson-databind 2.11.0.(diff)
Spring Boot 2.2.0: 2.10.0
Spring Boot 2.2.8: 2.10.4
Spring Boot 2.3.0: 2.11.0
so, it seems this behavior is Spring Boot 2.3.0 or later.

Change is documented in Spring Boot 2.3.0 RN's, in the section stating the changes in Jackson version, where it explicitly mentions "a change to the default formatting of java.util.Date and java.util.Calendar"

You can also use the following Spring Boot property to format your JSON dates: spring.jackson.date-format. Note this only works on java.util.Date and java.util.Calendar objects.

Related

Spring Boot YAML configuration with URL in key no longer loads correctly with version 2

I'm migrating my application from Spring Boot 1.5 to 2.0 and one of the YAML properties no longer loads correctly. The following configuration snippet:
myapp
serviceUrls:
'https://example.org/test': 'https://test.example.org/Endpoint'
is mapped to this configuration class:
#ConfigurationProperties(prefix = "myapp", ignoreUnknownFields = false)
public final class MyAppProperties {
private Map<String, String> serviceUrls = new HashMap<>();
//[...]
}
With Spring Boot 1.5, it loads as a map with https://example.org/test -> https://test.example.org/Endpoint;
but with Spring Boot 2.0, the colons and slashes disappear from map keys httpsexample.orgtest -> https://test.example.org/Endpoint.
I couldn't find any mention of this in the migration guide. Has YAML parsing changed in Spring Boot 2? Is there a better way to write YAML maps with URLs as keys?
I should have checked GitHub issues... someone reported a similar problem. The solution is to use the "bracket syntax", which is unfortunately barely documented, wrapping the keys within brackets:
myapp
serviceUrls:
'[https://example.org/test]': 'https://test.example.org/Endpoint'

Reload property value when external property file changes ,spring boot

I am using spring boot, and I have two external properties files, so that I can easily change its value.
But I hope spring app will reload the changed value when it is updated, just like reading from files. Since property file is easy enough to meet my need, I hope I don' nessarily need a db or file.
I use two different ways to load property value, code sample will like:
#RestController
public class Prop1Controller{
#Value("${prop1}")
private String prop1;
#RequestMapping(value="/prop1",method = RequestMethod.GET)
public String getProp() {
return prop1;
}
}
#RestController
public class Prop2Controller{
#Autowired
private Environment env;
#RequestMapping(value="/prop2/{sysId}",method = RequestMethod.GET)
public String prop2(#PathVariable String sysId) {
return env.getProperty("prop2."+sysId);
}
}
I will boot my application with
-Dspring.config.location=conf/my.properties
I'm afraid you will need to restart Spring context.
I think the only way to achieve your need is to enable spring-cloud. There is a refresh endpoint /refresh which refreshes the context and beans.
I'm not quite sure if you need a spring-cloud-config-server (its a microservice and very easy to build) where your config is stored(Git or svn). Or if its also useable just by the application.properties file in the application.
Here you can find the doc to the refresh scope and spring cloud.
You should be able to use Spring Cloud for that
Add this as a dependency
compile group: 'org.springframework.cloud', name: 'spring-cloud-starter', version: '1.1.2.RELEASE'
And then use #RefreshScope annotation
A Spring #Bean that is marked as #RefreshScope will get special treatment when there is a configuration change. This addresses the problem of stateful beans that only get their configuration injected when they are initialized. For instance if a DataSource has open connections when the database URL is changed via the Environment, we probably want the holders of those connections to be able to complete what they are doing. Then the next time someone borrows a connection from the pool he gets one with the new URL.
Also relevant if you have Spring Actuator
For a Spring Boot Actuator application there are some additional management endpoints:
POST to
/env to update the Environment and rebind #ConfigurationProperties and log levels
/refresh for re-loading the boot strap context and refreshing the #RefreshScope beans
Spring Cloud Doc
(1) Spring Cloud's RestartEndPoint
You may use the RestartEndPoint: Programatically restart Spring Boot application / Refresh Spring Context
RestartEndPoint is an Actuator EndPoint, bundled with spring-cloud-context.
However, RestartEndPoint will not monitor for file changes, you'll have to handle that yourself.
(2) devtools
I don't know if this is for a production application or not. You may hack devtools a little to do what you want.
Take a look at this other answer I wrote for another question: Force enable spring-boot DevTools when running Jar
Devtools monitors for file changes:
Applications that use spring-boot-devtools will automatically restart
whenever files on the classpath change.
Technically, devtools is built to only work within an IDE. With the hack, it also works when launched from a jar. However, I may not do that for a real production application, you decide if it fits your needs.
I know this is a old thread, but it will help someone in future.
You can use a scheduler to periodically refresh properties.
//MyApplication.java
#EnableScheduling
//application.properties
management.endpoint.refresh.enabled = true
//ContextRefreshConfig.java
#Autowired
private RefreshEndpoint refreshEndpoint;
#Scheduled(fixedDelay = 60000, initialDelay = 10000)
public Collection<String> refreshContext() {
final Collection<String> properties = refreshEndpoint.refresh();
LOGGER.log(Level.INFO, "Refreshed Properties {0}", properties);
return properties;
}
//add spring-cloud-starter to the pom file.
Attribues annotated with #Value is refreshed if the bean is annotated with #RefreshScope.
Configurations annotated with #ConfigurationProperties is refreshed without #RefreshScope.
Hope this will help.
You can follow the ContextRefresher.refresh() code implements.
public synchronized Set<String> refresh() {
Map<String, Object> before = extract(
this.context.getEnvironment().getPropertySources());
addConfigFilesToEnvironment();
Set<String> keys = changes(before,
extract(this.context.getEnvironment().getPropertySources())).keySet();
this.context.publishEvent(new EnvironmentChangeEvent(context, keys));
this.scope.refreshAll();
return keys;
}

Spring Data MongoDB auto create indexes not working

I am using Spring data Mongodb v1.6.2 and Spring 4.2.1. Today I noticed that #Indexed annotation on my entities did not trigger an index creation.
The entity is annotated with org.springframework.data.mongodb.core.mapping.Document and theorg.springframework.data.mongodb.core.mapping.Document is used.
#Document
public class Entity {
#Indexed(unique= true)
private String name;
}
After some investigation it appeared that MongoPersistentEntityIndexCreator did not receive the MappingContextEvent. Spring 4.2 altered the way generics are handled for ApplicationEvents.
Spring Data MongoDB fixed this in the following commit: https://github.com/spring-projects/spring-data-mongodb/commit/2a27eb74044d6480b228a216c1f93b2b0488c59a
The issue tracker can be found here: https://jira.spring.io/browse/DATAMONGO-1224
This was fixed in all version so upgrading to 1.6.3 fixed the issue.

spring-data-elasticsearch - Jackson can't serialize using global configuration

I'm developing a project using ElasticSearch and I'm having some problems with serialization/deserialization with Jackson. My project was created using JHipster, so, I'm using spring to store my entity to the database and to index in ElasticSearch. All entities and other objects can be (de)serialized with Jackson, except when I try to add it to ES.
This is my global configuration for Jackson:
#Configuration
public class JacksonConfiguration {
#Bean
Jackson2ObjectMapperBuilder jackson2ObjectMapperBuilder() {
SimpleModule timeModule = new JavaTimeModule();
timeModule.addSerializer(OffsetDateTime.class, JSR310DateTimeSerializer.INSTANCE);
timeModule.addSerializer(ZonedDateTime.class, JSR310DateTimeSerializer.INSTANCE);
timeModule.addSerializer(LocalDateTime.class, JSR310DateTimeSerializer.INSTANCE);
timeModule.addSerializer(Instant.class, JSR310DateTimeSerializer.INSTANCE);
timeModule.addDeserializer(LocalDate.class, JSR310LocalDateDeserializer.INSTANCE);
SimpleModule geoModule=new GeoModule();
geoModule.addSerializer(Point.class, PointSerializer.INSTANCE);
geoModule.addDeserializer(Point.class, PointDeserializer.INSTANCE);
return new Jackson2ObjectMapperBuilder()
.featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
.findModulesViaServiceLoader(true)
.modulesToInstall(timeModule,geoModule);
}
}
This configuration works fine, except when I try add an entity to ES, for example, PointSerializer is never called. The only way I can see this serializer running (and consequently indexing correctly) for ES is adding #JsonSerialize(using = PointSerializer.class) to the field. Why is it happening and how can I configure it globally?
It seems that Spring Data elasticsearch doesn't utilize the default spring Jackson2ObjectMapperBuilder for this. Per default this configuration is used:
https://github.com/spring-projects/spring-data-elasticsearch/blob/master/src/main/java/org/springframework/data/elasticsearch/core/DefaultEntityMapper.java
... which you can overwrite by providing some custom object mapper as described here:
https://github.com/spring-projects/spring-data-elasticsearch/wiki/Custom-ObjectMapper
Here you can of course directly use your Jackson ObjectMappers. For more details, have a look at this issue at the jhipster github repo:
https://github.com/jhipster/generator-jhipster/issues/2241#issuecomment-151933768

Need matching class for LoggersMvcEndpoint. in spring-boot 2.1.9 release

I am upgrading my project from spring-boot 1.5.12.release to 2.1.9.release. I am unable to find LoggersMvcEndpoint (https://docs.spring.io/spring-boot/docs/1.5.12.RELEASE/api/org/springframework/boot/actuate/endpoint/mvc/LoggersMvcEndpoint.html) in latest version.
In one of my controller I had this. Can some one help me to fix this.
public class LoggerController extends CloudRestTemplate {
#Autowired
LoggersMvcEndpoint loggerAPI;
#Override
public Object getFromInternalApi(final String param) {
return StringUtils.isEmpty(param) ? loggerAPI.invoke() : loggerAPI.get(param);
}
#Override
public Object postToInternalApi(final String param, final Object request) {
return loggerAPI.set(param, (Map<String, String>) request);
}
}
As per Spring docs here
Endpoint infrastructure
Spring Boot 2 brings a brand new endpoint
infrastructure that allows you to define one or several operations in
a technology independent fashion with support for Spring MVC, Spring
WebFlux and Jersey! Spring Boot 2 will have native support for Jersey
and writing an adapter for another JAX-RS implementation should be
easy as long as there is a way to programmatically register resources.
The new #Endpoint annotation declares this type to be an endpoint with
a mandatory, unique id. As we will see later, a bunch of properties
will be automatically inferred from that. No additional code is
required to expose this endpoint at /applications/loggers or as a
org.springframework.boot:type=Endpoint,name=Loggers JMX MBean.
Refer to documentation, it will help you further
and for your info LoggersMvcEndpoint was there until 2.0.0.M3 https://docs.spring.io/spring-boot/docs/2.0.0.M3/api/org/springframework/boot/actuate/endpoint/mvc/LoggersMvcEndpoint.html however there is no reference of deprecation in subsequent version's release notes of 2.0.0.M4
https://docs.spring.io/spring-boot/docs/2.0.0.M4/api/deprecated-list.html#class

Resources