spring-boot icon indicating copy to clipboard operation
spring-boot copied to clipboard

Actuator startup endpoint fails to serialize JSON when field visability is set to 'any'

Open iaptekar opened this issue 3 years ago • 6 comments

I have a demo project using Spring Boot 2.7.3. The only dependencies are the web and actuator starters. I try to start the application with a BufferingApplicationStartup and set management.endpoints.web.exposure.include=* in application.properties. This goes well and I can access startup metrics at /actuator/startup.

However, if I add the property spring.jackson.visibility.field=any to application.properties and hit the actuator/startup endpoint I get the following error:

com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Java 8 date/time type `java.time.Clock$SystemClock` not supported by default: add Module "com.fasterxml.jackson.datatype:jackson-datatype-jsr310" to enable handling (through reference chain: org.springframework.boot.actuate.startup.StartupEndpoint$StartupResponse["timeline"]
->org.springframework.boot.context.metrics.buffering.StartupTimeline["events"]
->java.util.Collections$UnmodifiableRandomAccessList[0]
->org.springframework.boot.context.metrics.buffering.StartupTimeline$TimelineEvent["step"]
->org.springframework.boot.context.metrics.buffering.BufferedStartupStep["recorder"]
->org.springframework.boot.context.metrics.buffering.BufferingApplicationStartup$$Lambda$62/0x0000000800c384d8["arg$1"]
->org.springframework.boot.context.metrics.buffering.BufferingApplicationStartup["clock"])
	at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:77) ~[jackson-databind-2.13.3.jar:2.13.3]
	at com.fasterxml.jackson.databind.SerializerProvider.reportBadDefinition(SerializerProvider.java:1300) ~[jackson-databind-2.13.3.jar:2.13.3]
...

None of the other spring.jackson properties I tried caused this error. Is there perhaps an issue with the ObjectMapper initialization for the startup actuator?

iaptekar avatar Sep 10 '22 12:09 iaptekar

This is somewhat related to #20291. I'm a little surprised that the same error isn't thrown during regular startup since java.time types are exposed in the getters as well.

@iaptekar Do you have the jackson-datatype-jsr310 library on your classpath? If you're using spring-boot-starter-json or spring-boot-starter-web then it should be pulled in. If not, have you tried adding it to see if it solves the issue?

philwebb avatar Sep 10 '22 17:09 philwebb

Yes I have jsr310 on the classpath. I am using it via spring-boot-starter-web. Though I have tried adding it as an extra dependency and even had a go at configuring the object mapper in a @Configuration. Nothing changes, as you soon as you add the Visibility.Any option you get that exception.

iaptekar avatar Sep 11 '22 10:09 iaptekar

Jackson's message is misleading. It suggests adding jackson-datatype-jsr310 for any java.time.* type, but jackson-datatype-jsr310 doesn't support serialisation of java.time.Clock$SystemClock. @iaptekar, you may want to raise a Jackson issue for this.

We could address this without tackling #20291 by mapping the StartupTimeline to a DTO where we could have complete control over the classes and their suitability for serialising to JSON. Arguably, this would make the startup endpoint more consistent with other endpoints such as the mappings endpoint and its various descriptors. It would, however, require a breaking change to org.springframework.boot.actuate.startup.StartupEndpoint.StartupResponse which is public API.

wilkinsona avatar Sep 14 '22 18:09 wilkinsona

We might be able to use @JsonSerialize to take control of how StartupResponse is serialized and avoid the unwanted field serialization.

wilkinsona avatar Oct 31 '22 17:10 wilkinsona

I have a branch at https://github.com/philwebb/spring-boot/tree/gh-32297 that shows how @JsonSerialize could work. I'm not totally sure if we should take this approach or not.

philwebb avatar Nov 01 '22 20:11 philwebb

I like the approach. It seems low risk and addresses the problem without making a breaking change to public API. One for 3.1.12?

wilkinsona avatar Apr 26 '24 13:04 wilkinsona

Sorry guys If I probably go off topic in this thread, but using as a reference the https://github.com/spring-projects/spring-boot/issues/20291 and also the field visibility, the isolation isn't behaving probably properly? if you customize the Jackson2ObjectMapperBuilderCustomizer, for example:

public Jackson2ObjectMapperBuilderCustomizer jacksonMapperBuilderCustomizer() {
        return builder -> builder
                .defaultTyping(ObjectMapper
                        .DefaultTypeResolverBuilder
                        .construct(ObjectMapper.DefaultTyping.NON_FINAL, LaissezFaireSubTypeValidator.instance)
                        .init(JsonTypeInfo.Id.CLASS, null)
                        .inclusion(JsonTypeInfo.As.PROPERTY));
    }

it will add the "_class" the json output of the /actuator endpoints, and for example it breaks "Intellij Actuator tab support", but the main issue is that shouldn't that be isolated?

Thanks

jsantana3c avatar Jun 03 '24 10:06 jsantana3c

It's hard to say without some more context. If you believe you've found a bug related to Actuator's isolated object mapper, please open a new issue with a minimal sample that reproduces the problem and we can take a look.

wilkinsona avatar Jun 03 '24 10:06 wilkinsona

thanks! I'll create a new issue, I thought it was probably related to this.

joaquinjsb avatar Jun 03 '24 11:06 joaquinjsb

The last few comments have reminded me that, as of 3.0, this is only a problem when both spring.jackson.visibility.field=any and management.endpoints.jackson.isolated-object-mapper=false are configured. As such, I'm not sure that we should do anything and I'm in favour of closing this one as superseded by https://github.com/spring-projects/spring-boot/issues/20291.

wilkinsona avatar Jun 03 '24 12:06 wilkinsona

that could be the same issue I'm being affected, because I have the default management.endpoints.jackson.isolated-object-mapper which is true, and by overriding it to true or leaving default, the changes I do to my Jackson2ObjectMapperBuilderCustomizer is being passed to the objectmapper on the actuator side., do you still think I should open another issue?

joaquinjsb avatar Jun 03 '24 12:06 joaquinjsb

Yes please, @joaquinjsb. It's better to keep things separate to begin with and to combine them if necessary than to start trying to track two potentially different problems in the same issue.

wilkinsona avatar Jun 03 '24 12:06 wilkinsona

and I'm in favour of closing this one

I agree.

mhalbritter avatar Jun 04 '24 07:06 mhalbritter