Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Serialize InvalidFormatException cause infinite recursion #1413

Closed
inxi-pc opened this issue Oct 13, 2016 · 7 comments
Closed

Serialize InvalidFormatException cause infinite recursion #1413

inxi-pc opened this issue Oct 13, 2016 · 7 comments

Comments

@inxi-pc
Copy link

inxi-pc commented Oct 13, 2016

Jackson version: 2.8.1

Hi,

I use jackson as a MessageBodyWriter and MessageBodyReader in jesery framwork. I am doing a

restful application. But I meet a terrible issue when i get InvalidFormatiotException and convert it

to a response, Infinite recursion (StackOverflowError) error reporting!

org.glassfish.jersey.server.internal.process.MappableException: com.fasterxml.jackson.databind.JsonMappingException: Infinite recursion (StackOverflowError) (through reference chain: com.fasterxml.jackson.databind.ObjectMapper["factory"]->com.fasterxml.jackson.databind.MappingJsonFactory["codec"]->com.fasterxml.jackson.databind.ObjectMapper["factory"]->com.fasterxml.jackson.databind.MappingJsonFactory["codec"]->com.fasterxml.jackson.databind.ObjectMapper["factory"]->com.fasterxml.jackson.databind.MappingJsonFactory["codec"]->com.fasterxml.jackson.databind.ObjectMapper["factory"]->com.fasterxml.jackson.databind.MappingJsonFactory["codec"]->com.fasterxml.jackson.databind.ObjectMapper["factory"]->com.fasterxml.jackson.databind.MappingJsonFactory["codec"]->com.fasterxml.jackson.databind.ObjectMapper["factory"]->com.fasterxml.jackson.databind.MappingJsonFactory["codec"]->com.fasterxml.jackson.databind.ObjectMapper["factory"]->com.fasterxml.jackson.databind.MappingJsonFactory["codec"]->com.fasterxml.jackson.databind.ObjectMapper["factory"]->com.fasterxml.jackson.databind.MappingJsonFactory["codec"]->com.fasterxml.jackson.databind.ObjectMapper["factory"]->com.fasterxml.jackson.databind.MappingJsonFactory["codec"]->com.fasterxml.jackson.databind.ObjectMapper["factory"]->com.fasterxml.jackson.databind.MappingJsonFactory["codec"]->com.fasterxml.jackson.databind.ObjectMapper["factory"]->com.fasterxml.jackson.databind.MappingJsonFactory["codec"]->com.fasterxml.jackson.databind.ObjectMapper["factory"]->com.fasterxml.jackson.databind.MappingJsonFactory["codec"]->com.fasterxml.jackson.databind.ObjectMapper["factory"]->com.fasterxml.jackson.databind.MappingJsonFactory["codec"]

have ignored very long repeat information.

I have a POJO:

public class Post extends Domain {

    @PrimaryKey
    private Integer post_id;
    private Integer category_id;
    private Integer user_id;
    private String post_title;
    private String post_content;
    private Boolean post_published;
    private Boolean post_enabled;
    private String post_created_at;
    private String post_updated_at;

    public Post() {
        this.post_id = null;
        this.category_id = null;
        this.user_id = null;
        this.post_title = null;
        this.post_content = null;
        this.post_published = null;
        this.post_enabled = null;
        this.post_created_at = null;
        this.post_updated_at = null;
    }

    @JsonCreator
    public Post(@JsonProperty("post_id") Integer postId,
                @JsonProperty("category_id") Integer categoryId,
                @JsonProperty("user_id") Integer userId,
                @JsonProperty("post_title") String postTitle,
                @JsonProperty("post_content") String postContent,
                @JsonProperty("post_published") Boolean postPublished,
                @JsonProperty("post_enabled") Boolean postEnabled,
                @JsonProperty("post_created_at") String postCreatedAt,
                @JsonProperty("post_updated_at") String postUpdatedAt) {
        this.post_id = postId;
        this.category_id = categoryId;
        this.user_id = userId;
        this.post_title = postTitle;
        this.post_content = postContent;
        this.post_published = postPublished;
        this.post_enabled = postEnabled;
        this.post_created_at = postCreatedAt;
        this.post_updated_at = postUpdatedAt;
    }
    // omit setter and getter
}

In normal situation, i transfer a normal json like {"post_id": 1, "post_title": "test"'}

(I want to update a post), it works fine. But when i transfer a type mismatch json like

{"post_id": "test", "post_title": "test"'}, the terrible situation appeared.

I have a exception mapper to mapper a exception to a response, so in this case, when the type

is mismatch, jackson will occur a InvalidFormatException:

com.fasterxml.jackson.databind.exc.InvalidFormatException: Can not deserialize value of type java.lang.Integer from String "test": not a valid Integer value
 at [Source: org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$UnCloseableInputStream@77d4bc0b; line: 2, column: 13] (through reference chain: myblog.domain.Post["post_id"])
    at com.fasterxml.jackson.databind.exc.InvalidFormatException.from(InvalidFormatException.java:74)
    at com.fasterxml.jackson.databind.DeserializationContext.weirdStringException(DeserializationContext.java:1374)
    at com.fasterxml.jackson.databind.DeserializationContext.handleWeirdStringValue(DeserializationContext.java:925)
    at com.fasterxml.jackson.databind.deser.std.StdDeserializer._parseInteger(StdDeserializer.java:454)
    at com.fasterxml.jackson.databind.deser.std.NumberDeserializers$IntegerDeserializer.deserialize(NumberDeserializers.java:308)
    at com.fasterxml.jackson.databind.deser.std.NumberDeserializers$IntegerDeserializer.deserialize(NumberDeserializers.java:286)
    at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:490)
    at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:95)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:276)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:140)
    at com.fasterxml.jackson.databind.ObjectReader._bind(ObjectReader.java:1579)
    at com.fasterxml.jackson.databind.ObjectReader.readValue(ObjectReader.java:961)
    at com.fasterxml.jackson.jaxrs.base.ProviderBase.readFrom(ProviderBase.java:808)
    at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$TerminalReaderInterceptor.invokeReadFrom(ReaderInterceptorExecutor.java:256)
    at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$TerminalReaderInterceptor.aroundReadFrom(ReaderInterceptorExecutor.java:235)
    at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor.proceed(ReaderInterceptorExecutor.java:155)
    at org.glassfish.jersey.server.internal.MappableExceptionWrapperInterceptor.aroundReadFrom(MappableExceptionWrapperInterceptor.java:74)
    at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor.proceed(ReaderInterceptorExecutor.java:155)
    at org.glassfish.jersey.message.internal.MessageBodyFactory.readFrom(MessageBodyFactory.java:1085)
    at org.glassfish.jersey.message.internal.InboundMessageContext.readEntity(InboundMessageContext.java:874)
    at org.glassfish.jersey.server.ContainerRequest.readEntity(ContainerRequest.java:271)
    at org.glassfish.jersey.server.internal.inject.EntityParamValueFactoryProvider$EntityValueFactory.provide(EntityParamValueFactoryProvider.java:96)
    at org.glassfish.jersey.server.spi.internal.ParamValueFactoryWithSource.provide(ParamValueFactoryWithSource.java:71)
    at org.glassfish.jersey.server.spi.internal.ParameterValueHelper.getParameterValues(ParameterValueHelper.java:94)
    at org.glassfish.jersey.server.model.internal.JavaResourceMethodDispatcherProvider$AbstractMethodParamInvoker.getParamValues(JavaResourceMethodDispatcherProvider.java:127)
    at org.glassfish.jersey.server.model.internal.JavaResourceMethodDispatcherProvider$ResponseOutInvoker.doDispatch(JavaResourceMethodDispatcherProvider.java:160)
    at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.dispatch(AbstractJavaResourceMethodDispatcher.java:99)
    at org.glassfish.jersey.server.model.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:389)
    at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:347)
    at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:102)
    at org.glassfish.jersey.server.ServerRuntime$2.run(ServerRuntime.java:326)
    at org.glassfish.jersey.internal.Errors$1.call(Errors.java:271)
    at org.glassfish.jersey.internal.Errors$1.call(Errors.java:267)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:315)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:297)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:267)
    at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:317)
    at org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:305)
    at org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:1154)
    at org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpContainer.service(GrizzlyHttpContainer.java:384)
    at org.glassfish.grizzly.http.server.HttpHandler$1.run(HttpHandler.java:224)
    at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:591)
    at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.run(AbstractThreadPool.java:571)
    at java.lang.Thread.run(Thread.java:745)

And then i use a InternalServerException to pack this exception, and then set response entity by

using this new exception:

@Provider
public class MyExceptionMapper implements ExceptionMapper<Exception> {

    public Response toResponse(Exception ex) {


        if (App.isDebug()) {
            ex.printStackTrace();
            return Response.status(500)
                    .entity(ex)
                    .type(MediaType.APPLICATION_JSON)
                    .build();
        } else {
            return Response.status(500)
                    .entity(ex)
                    .type(MediaType.APPLICATION_JSON)
                    .build();
        }
    }
}

Then, as you see, the infinite recursion appeared. Why i get this terrible error? If i dont pack the

InvalidFormatException and use another exception to wrap the response entity, every thins is ok!

Hopeful to get the reply, i debugged whole day, StackOverFlow is really a disaster.

Thanks

@inxi-pc
Copy link
Author

inxi-pc commented Oct 13, 2016

i found the issue reason.

This is the packed exception struct:

// Root InternelServerException
  Cause
     // Children InvalidFormatException  
     Cause
     StrakTrace
     _targetType    
  StrakTrace
  ....

let me show the progress of serialize, the key is in InvalidFormatException field _targetType.

_targetType is ObjectMapper instance, it has a field _jsonFactory, it is JsonFactory instance,
the JsonFactory has a field _objectCodec, it is ObjectMapper instance and is the reference of _targetType.

When serialize the _targetType(ObjectMapper) fields, jackson will serialize the _jsonFactory(JsonFactory), then jackson serialize _jsonFactory(JsonFactory) fields, the _objectCodec(ObjectMapper) will be serialized, because it is reference of _targetType, so the endless loop generated!

See the below picture:

image

image

@inxi-pc
Copy link
Author

inxi-pc commented Oct 13, 2016

So i guess, if two class has reference of each other, then the serialization will be failed. I think it is a

terrible problem. Please check this!!

@cowtowncoder
Copy link
Member

@inxi-pc Have you tried this with the latest patch version? This looks like #1368 which was fixed in 2.8.3.

@cowtowncoder
Copy link
Member

Can not reproduce with 2.8[.4], assuming fixed, closed.

@inxi-pc
Copy link
Author

inxi-pc commented Oct 14, 2016

@cowtowncoder i checked #1368 patch content, @JsonIgnore added on the field _processor, in this case, the field _targetType is issue, so i think we need add @JsonIgnore on this field also.

let me try this version(2.8.3), and then reply here. Thanks!

@inxi-pc
Copy link
Author

inxi-pc commented Oct 14, 2016

@cowtowncoder yes, i try version(2.8.3), no stack overflow, but i am confusing, i dont find the ignore on the field _targetType on InvalidFormatException, how it is be ignored? i debug and find this field is not be serialize.

@inxi-pc
Copy link
Author

inxi-pc commented Oct 14, 2016

@cowtowncoder Ok Ok, i found, i see the wrong field. The issue is in _processor field, not _targetType. Everything is ok now, thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants