Skip to content
This repository has been archived by the owner on Feb 23, 2023. It is now read-only.

ErrorAttributes serialization exception #1084

Closed
ivangfr opened this issue Sep 25, 2021 · 2 comments
Closed

ErrorAttributes serialization exception #1084

ivangfr opened this issue Sep 25, 2021 · 2 comments
Assignees
Labels
type: compatibility Native image compatibility issue
Milestone

Comments

@ivangfr
Copy link

ivangfr commented Sep 25, 2021

Hi, I've added spring-native to this project spring-cloud-stream-event-routing-cloudevents.

In general, it works fine except when I submit an invalid request according to what javax.validation.constraints is checking.

For instance, the field titolo in CreateRAINewsRequest must not be blank

@Data
public class CreateRAINewsRequest {

    @NotBlank
    private String titolo;
}

It's a valid request: http :9080/api/news/rai titolo="Sciopero della metropolitana di Roma"
It's an invalid request: http :9080/api/news/rai titolo2="Sciopero della metropolitana di Roma"

When running the Docker native container and submitting an invalid request, the response is

HTTP/1.1 500 Internal Server Error

and the following exception is logged

ERROR 1 --- [ctor-http-nio-4] o.s.w.s.adapter.HttpWebHandlerAdapter    : [bbd9a804-3] 500 Server Error for HTTP POST "/api/news/rai"

org.springframework.core.codec.CodecException: Type definition error: [simple type, class org.springframework.validation.beanvalidation.SpringValidatorAdapter$ViolationFieldError]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class org.springframework.validation.beanvalidation.SpringValidatorAdapter$ViolationFieldError and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: java.util.LinkedHashMap["errors"]->java.util.Collections$UnmodifiableRandomAccessList[0])
	at org.springframework.http.codec.json.AbstractJackson2Encoder.encodeValue(AbstractJackson2Encoder.java:226) ~[na:na]
	at org.springframework.http.codec.json.AbstractJackson2Encoder.lambda$encode$0(AbstractJackson2Encoder.java:150) ~[na:na]
	at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onNext(FluxMapFuseable.java:113) ~[na:na]
	at reactor.core.publisher.Operators$ScalarSubscription.request(Operators.java:2398) ~[com.mycompany.producerservice.ProducerServiceApplication:3.4.11]
	at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.request(FluxMapFuseable.java:169) ~[na:na]
	at reactor.core.publisher.MonoSingle$SingleSubscriber.doOnRequest(MonoSingle.java:103) ~[na:na]
	at reactor.core.publisher.Operators$MonoInnerProducerBase.request(Operators.java:2731) ~[com.mycompany.producerservice.ProducerServiceApplication:3.4.11]
	at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.set(Operators.java:2194) ~[com.mycompany.producerservice.ProducerServiceApplication:3.4.11]
	at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.onSubscribe(Operators.java:2068) ~[com.mycompany.producerservice.ProducerServiceApplication:3.4.11]
	at reactor.core.publisher.MonoSingle$SingleSubscriber.onSubscribe(MonoSingle.java:115) ~[na:na]
	at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onSubscribe(FluxMapFuseable.java:96) ~[na:na]
	at reactor.core.publisher.MonoJust.subscribe(MonoJust.java:55) ~[na:na]
	at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:64) ~[na:na]
	at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:157) ~[com.mycompany.producerservice.ProducerServiceApplication:3.4.11]
	at reactor.core.publisher.FluxPeekFuseable$PeekFuseableSubscriber.onNext(FluxPeekFuseable.java:210) ~[na:na]
	at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1816) ~[com.mycompany.producerservice.ProducerServiceApplication:3.4.11]
	at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:151) ~[com.mycompany.producerservice.ProducerServiceApplication:3.4.11]
	at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onNext(FluxSwitchIfEmpty.java:74) ~[na:na]
	at reactor.core.publisher.MonoNext$NextSubscriber.onNext(MonoNext.java:82) ~[com.mycompany.producerservice.ProducerServiceApplication:3.4.11]
	at reactor.core.publisher.FluxConcatArray$ConcatArraySubscriber.onNext(FluxConcatArray.java:201) ~[com.mycompany.producerservice.ProducerServiceApplication:3.4.11]
	at reactor.core.publisher.Operators$ScalarSubscription.request(Operators.java:2398) ~[com.mycompany.producerservice.ProducerServiceApplication:3.4.11]
	at reactor.core.publisher.FluxConcatArray$ConcatArraySubscriber.onSubscribe(FluxConcatArray.java:193) ~[com.mycompany.producerservice.ProducerServiceApplication:3.4.11]
	at reactor.core.publisher.MonoJust.subscribe(MonoJust.java:55) ~[na:na]
	at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52) ~[na:na]
	at reactor.core.publisher.Mono.subscribe(Mono.java:4399) ~[com.mycompany.producerservice.ProducerServiceApplication:3.4.11]
	at reactor.core.publisher.FluxConcatArray$ConcatArraySubscriber.onComplete(FluxConcatArray.java:258) ~[com.mycompany.producerservice.ProducerServiceApplication:3.4.11]
	at reactor.core.publisher.FluxConcatArray.subscribe(FluxConcatArray.java:78) ~[na:na]
	at reactor.core.publisher.Mono.subscribe(Mono.java:4399) ~[com.mycompany.producerservice.ProducerServiceApplication:3.4.11]
	at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onError(FluxOnErrorResume.java:103) ~[na:na]
	at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onError(FluxOnErrorResume.java:106) ~[na:na]
	at reactor.core.publisher.FluxOnAssembly$OnAssemblySubscriber.onError(FluxOnAssembly.java:493) ~[na:na]
	at reactor.core.publisher.Operators.error(Operators.java:198) ~[na:na]
	at reactor.core.publisher.MonoError.subscribe(MonoError.java:53) ~[na:na]
	at reactor.core.publisher.Mono.subscribe(Mono.java:4399) ~[com.mycompany.producerservice.ProducerServiceApplication:3.4.11]
	at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onError(FluxOnErrorResume.java:103) ~[na:na]
	at reactor.core.publisher.FluxOnAssembly$OnAssemblySubscriber.onError(FluxOnAssembly.java:493) ~[na:na]
	at reactor.core.publisher.FluxPeek$PeekSubscriber.onError(FluxPeek.java:222) ~[na:na]
	at reactor.core.publisher.FluxDoOnEach$DoOnEachSubscriber.onError(FluxDoOnEach.java:195) ~[na:na]
	at reactor.core.publisher.MonoFlatMap$FlatMapMain.onError(MonoFlatMap.java:172) ~[com.mycompany.producerservice.ProducerServiceApplication:3.4.11]
	at reactor.core.publisher.MonoFlatMap$FlatMapMain.secondError(MonoFlatMap.java:192) ~[com.mycompany.producerservice.ProducerServiceApplication:3.4.11]
	at reactor.core.publisher.MonoFlatMap$FlatMapInner.onError(MonoFlatMap.java:259) ~[com.mycompany.producerservice.ProducerServiceApplication:3.4.11]
	at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onError(FluxOnErrorResume.java:106) ~[na:na]
	at reactor.core.publisher.Operators.error(Operators.java:198) ~[na:na]
	at reactor.core.publisher.MonoError.subscribe(MonoError.java:53) ~[na:na]
	at reactor.core.publisher.Mono.subscribe(Mono.java:4399) ~[com.mycompany.producerservice.ProducerServiceApplication:3.4.11]
	at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onError(FluxOnErrorResume.java:103) ~[na:na]
	at reactor.core.publisher.FluxPeek$PeekSubscriber.onError(FluxPeek.java:222) ~[na:na]
	at reactor.core.publisher.FluxPeek$PeekSubscriber.onError(FluxPeek.java:222) ~[na:na]
	at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.onError(MonoIgnoreThen.java:270) ~[com.mycompany.producerservice.ProducerServiceApplication:3.4.11]
	at reactor.core.publisher.MonoFlatMap$FlatMapMain.onError(MonoFlatMap.java:172) ~[com.mycompany.producerservice.ProducerServiceApplication:3.4.11]
	at reactor.core.publisher.MonoZip$ZipInner.onError(MonoZip.java:350) ~[com.mycompany.producerservice.ProducerServiceApplication:3.4.11]
	at reactor.core.publisher.MonoPeekTerminal$MonoTerminalPeekSubscriber.onError(MonoPeekTerminal.java:258) ~[na:na]
	at reactor.core.publisher.Operators$MonoSubscriber.onError(Operators.java:1863) ~[com.mycompany.producerservice.ProducerServiceApplication:3.4.11]
	at reactor.core.publisher.FluxPeek$PeekSubscriber.onError(FluxPeek.java:222) ~[na:na]
	at reactor.core.publisher.FluxPeek$PeekSubscriber.onNext(FluxPeek.java:194) ~[na:na]
	at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onNext(FluxSwitchIfEmpty.java:74) ~[na:na]
	at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onNext(FluxOnErrorResume.java:79) ~[na:na]
	at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1816) ~[com.mycompany.producerservice.ProducerServiceApplication:3.4.11]
	at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:151) ~[com.mycompany.producerservice.ProducerServiceApplication:3.4.11]
	at reactor.core.publisher.FluxContextWrite$ContextWriteSubscriber.onNext(FluxContextWrite.java:107) ~[na:na]
	at reactor.core.publisher.FluxMapFuseable$MapFuseableConditionalSubscriber.onNext(FluxMapFuseable.java:295) ~[na:na]
	at reactor.core.publisher.FluxFilterFuseable$FilterFuseableConditionalSubscriber.onNext(FluxFilterFuseable.java:337) ~[na:na]
	at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1816) ~[com.mycompany.producerservice.ProducerServiceApplication:3.4.11]
	at reactor.core.publisher.MonoCollect$CollectSubscriber.onComplete(MonoCollect.java:159) ~[na:na]
	at reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:142) ~[na:na]
	at reactor.core.publisher.FluxPeek$PeekSubscriber.onComplete(FluxPeek.java:260) ~[na:na]
	at reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:142) ~[na:na]
	at reactor.netty.channel.FluxReceive.onInboundComplete(FluxReceive.java:400) ~[com.mycompany.producerservice.ProducerServiceApplication:1.0.12]
	at reactor.netty.channel.ChannelOperations.onInboundComplete(ChannelOperations.java:419) ~[com.mycompany.producerservice.ProducerServiceApplication:1.0.12]
	at reactor.netty.http.server.HttpServerOperations.onInboundNext(HttpServerOperations.java:589) ~[na:na]
	at reactor.netty.channel.ChannelOperationsHandler.channelRead(ChannelOperationsHandler.java:93) ~[na:na]
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379) ~[na:na]
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365) ~[na:na]
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357) ~[na:na]
	at reactor.netty.http.server.HttpTrafficHandler.channelRead(HttpTrafficHandler.java:261) ~[na:na]
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379) ~[na:na]
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365) ~[na:na]
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357) ~[na:na]
	at io.netty.channel.CombinedChannelDuplexHandler$DelegatingChannelHandlerContext.fireChannelRead(CombinedChannelDuplexHandler.java:436) ~[na:na]
	at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:324) ~[na:na]
	at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:296) ~[na:na]
	at io.netty.channel.CombinedChannelDuplexHandler.channelRead(CombinedChannelDuplexHandler.java:251) ~[na:na]
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379) ~[na:na]
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365) ~[na:na]
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357) ~[na:na]
	at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410) ~[na:na]
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379) ~[na:na]
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365) ~[na:na]
	at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919) ~[na:na]
	at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:166) ~[na:na]
	at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:719) ~[na:na]
	at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:655) ~[na:na]
	at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:581) ~[na:na]
	at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:493) ~[na:na]
	at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:986) ~[na:na]
	at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) ~[na:na]
	at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) ~[na:na]
	at java.lang.Thread.run(Thread.java:829) ~[na:na]
	at com.oracle.svm.core.thread.JavaThreads.threadStartRoutine(JavaThreads.java:596) ~[na:na]
	at com.oracle.svm.core.posix.thread.PosixJavaThreads.pthreadStartRoutine(PosixJavaThreads.java:192) ~[na:na]
Caused by: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class org.springframework.validation.beanvalidation.SpringValidatorAdapter$ViolationFieldError and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: java.util.LinkedHashMap["errors"]->java.util.Collections$UnmodifiableRandomAccessList[0])
	at com.fasterxml.jackson.databind.SerializerProvider.reportBadDefinition(SerializerProvider.java:1276) ~[na:na]
	at com.fasterxml.jackson.databind.DatabindContext.reportBadDefinition(DatabindContext.java:400) ~[na:na]
	at com.fasterxml.jackson.databind.ser.impl.UnknownSerializer.failForEmpty(UnknownSerializer.java:71) ~[na:na]
	at com.fasterxml.jackson.databind.ser.impl.UnknownSerializer.serialize(UnknownSerializer.java:33) ~[na:na]
	at com.fasterxml.jackson.databind.ser.impl.IndexedListSerializer.serializeContents(IndexedListSerializer.java:119) ~[na:na]
	at com.fasterxml.jackson.databind.ser.impl.IndexedListSerializer.serialize(IndexedListSerializer.java:79) ~[na:na]
	at com.fasterxml.jackson.databind.ser.impl.IndexedListSerializer.serialize(IndexedListSerializer.java:18) ~[na:na]
	at com.fasterxml.jackson.databind.ser.std.MapSerializer.serializeFields(MapSerializer.java:808) ~[na:na]
	at com.fasterxml.jackson.databind.ser.std.MapSerializer.serializeWithoutTypeInfo(MapSerializer.java:764) ~[na:na]
	at com.fasterxml.jackson.databind.ser.std.MapSerializer.serialize(MapSerializer.java:720) ~[na:na]
	at com.fasterxml.jackson.databind.ser.std.MapSerializer.serialize(MapSerializer.java:35) ~[na:na]
	at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider._serialize(DefaultSerializerProvider.java:480) ~[na:na]
	at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:400) ~[na:na]
	at com.fasterxml.jackson.databind.ObjectWriter$Prefetch.serialize(ObjectWriter.java:1510) ~[na:na]
	at com.fasterxml.jackson.databind.ObjectWriter.writeValue(ObjectWriter.java:1006) ~[na:na]
	at org.springframework.http.codec.json.AbstractJackson2Encoder.encodeValue(AbstractJackson2Encoder.java:222) ~[na:na]
	... 99 common frames omitted

The expected response (as it happens with Docker JVM) should be something like

HTTP/1.1 400 Bad Request
{
    "error": "Bad Request",
    "errors": [
        {
            "arguments": [
                {
                    "arguments": null,
                    "code": "titolo",
                    "codes": [
                        "createRAINewsRequest.titolo",
                        "titolo"
                    ],
                    "defaultMessage": "titolo"
                }
            ],
            "bindingFailure": false,
            "code": "NotBlank",
            "codes": [
                "NotBlank.createRAINewsRequest.titolo",
                "NotBlank.titolo",
                "NotBlank.java.lang.String",
                "NotBlank"
            ],
            "defaultMessage": "must not be blank",
            "field": "titolo",
            "objectName": "createRAINewsRequest",
            "rejectedValue": null
        }
    ],
    "exception": "org.springframework.web.bind.support.WebExchangeBindException",
    "message": "Validation failed for argument at index 0 in method: public reactor.core.publisher.Mono<com.mycompany.producerservice.kafka.news.event.RAINewsCreated> com.mycompany.producerservice.rest.news.NewsController.createRAINews(com.mycompany.producerservice.rest.news.dto.CreateRAINewsRequest), with 1 error(s): [Field error in object 'createRAINewsRequest' on field 'titolo': rejected value [null]; codes [NotBlank.createRAINewsRequest.titolo,NotBlank.titolo,NotBlank.java.lang.String,NotBlank]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [createRAINewsRequest.titolo,titolo]; arguments []; default message [titolo]]; default message [must not be blank]] ",
    "path": "/api/news/rai",
    "requestId": "af28deb4-1",
    "status": 400,
    "timestamp": "..."
}

I've tried to add the following but it didn't help

@NativeHint(
        types = @TypeHint(
                ...
                typeNames = "org.springframework.validation.beanvalidation.SpringValidatorAdapter$ViolationFieldError"
        )
)

How to reproduce

  • git clone https://github.com/ivangfr/spring-cloud-stream-event-routing-cloudevents.git
  • cd spring-cloud-stream-event-routing-cloudevents
  • ./mvnw clean spring-boot:build-image --projects producer-service -DskipTests
  • docker-compose up -d
  • run docker container
    docker run --rm --name producer-service -p 9080:9080 \
        -e KAFKA_HOST=kafka -e KAFKA_PORT=9092 \
        --network=spring-cloud-stream-event-routing-cloudevents_default \
        ivanfranchin/producer-service:1.0.0
    
  • submit valid request: http :9080/api/news/rai titolo="Sciopero della metropolitana di Roma"
  • submit invalid request: http :9080/api/news/rai titolo2="Sciopero della metropolitana di Roma"

Configuration

  • Mac OS X
  • Java 11
  • Spring Boot 2.5.6
  • Spring Native 0.10.5
@sdeleuze
Copy link
Contributor

sdeleuze commented Feb 1, 2022

Sorry for the delay, I can't reproduce anymore (other issues occurs before), could you please provide an up to date minimal repro (without Spring Cloud since it seems not related)?

@sdeleuze sdeleuze added the status: waiting-for-feedback We need additional information before we can continue label Feb 1, 2022
@ivangfr
Copy link
Author

ivangfr commented Feb 1, 2022

@sdeleuze , indeed. The current example is throwing the following compilation error when trying to build the Docker native image for producer-service and consumer-service apps.

[INFO] -------------------------------------------------------------
[ERROR] COMPILATION ERROR :
[INFO] -------------------------------------------------------------
[ERROR] /spring-cloud-stream-event-routing-cloudevents/producer-service/target/generated-runtime-sources/spring-aot/src/main/java/org/springframework/cloud/stream/config/ContextBootstrapInitializer.java:[72,111] org.springframework.cloud.stream.config.BindersHealthIndicatorAutoConfiguration.BindersHealthContributor has private access in org.springframework.cloud.stream.config.BindersHealthIndicatorAutoConfiguration
[ERROR] /spring-cloud-stream-event-routing-cloudevents/producer-service/target/generated-runtime-sources/spring-aot/src/main/java/org/springframework/cloud/stream/config/ContextBootstrapInitializer.java:[78,253] org.springframework.cloud.stream.config.BindersHealthIndicatorAutoConfiguration.BindersHealthContributor has private access in org.springframework.cloud.stream.config.BindersHealthIndicatorAutoConfiguration
[INFO] 2 errors
[INFO] -------------------------------------------------------------

About the problem described in this issue (submitting an invalid request according to what javax.validation.constraints is checking), I've found a way to fix it by adding

@TypeHint(
        ...
        typeNames = "org.springframework.validation.beanvalidation.SpringValidatorAdapter$ViolationFieldError",
        access = { TypeAccess.PUBLIC_CONSTRUCTORS, TypeAccess.PUBLIC_METHODS }
)

like I did here

However, I couldn't try in current example provided in this issue, as I am not able to compile the Docker native images.

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels Feb 1, 2022
@sdeleuze sdeleuze self-assigned this Feb 1, 2022
@sdeleuze sdeleuze added the type: compatibility Native image compatibility issue label Feb 1, 2022
@sdeleuze sdeleuze added this to the 0.11.3 milestone Feb 1, 2022
@sdeleuze sdeleuze removed status: waiting-for-triage An issue we've not yet triaged or decided on status: feedback-provided Feedback has been provided labels Feb 1, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
type: compatibility Native image compatibility issue
Development

No branches or pull requests

3 participants