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

Infinite recursion when deserializing a class extending a Map, with a recursive value type. #1658

Closed
newkek opened this issue Jun 14, 2017 · 10 comments
Milestone

Comments

@newkek
Copy link

newkek commented Jun 14, 2017

Hello, I am using jackson-databind 2.8.8, and have a class with an unusual definition (extending a Map, where the values are of the type of the same class). It seems like I am facing an infinite recursion issue.

To reproduce you can re-use or inspire from the class defined here.

Then, when executing the following code:

        Tree t = new Tree("hello", new Tree("world"));

        ObjectMapper om = new ObjectMapper();
        final TypeResolverBuilder<?> typer = new StdTypeResolverBuilder()
                .init(JsonTypeInfo.Id.CLASS, null)
                .inclusion(JsonTypeInfo.As.PROPERTY)
                .typeProperty(GraphSONTokens.CLASS);
        om.setDefaultTyping(typer);

        String res = om.writeValueAsString(t);
        Object tRead = om.readValue(res, Tree.class);

When calling readValue() the mapper throws a StackOverflowException , here's the stacktrace:

java.lang.StackOverflowError
	at org.apache.tinkerpop.shaded.jackson.databind.type.MapLikeType.equals(MapLikeType.java:305)
	at org.apache.tinkerpop.shaded.jackson.databind.type.ResolvedRecursiveType.equals(ResolvedRecursiveType.java:110)
	at org.apache.tinkerpop.shaded.jackson.databind.type.MapLikeType.equals(MapLikeType.java:305)
	at org.apache.tinkerpop.shaded.jackson.databind.type.ResolvedRecursiveType.equals(ResolvedRecursiveType.java:110)
	at org.apache.tinkerpop.shaded.jackson.databind.type.MapLikeType.equals(MapLikeType.java:305)
	at org.apache.tinkerpop.shaded.jackson.databind.type.ResolvedRecursiveType.equals(ResolvedRecursiveType.java:110)
	at org.apache.tinkerpop.shaded.jackson.databind.type.MapLikeType.equals(MapLikeType.java:305)
	at org.apache.tinkerpop.shaded.jackson.databind.type.ResolvedRecursiveType.equals(ResolvedRecursiveType.java:110)
	at org.apache.tinkerpop.shaded.jackson.databind.type.MapLikeType.equals(MapLikeType.java:305)
[...]

Looking briefly into the code, it seems like because of the recursive definition of the class, the equals call in MapLikeType may never get out of this loop. Any idea?

Thanks.

@newkek
Copy link
Author

newkek commented Jun 14, 2017

This also occurs with 2.8.7 fwiw.

@cowtowncoder
Copy link
Member

I think I'd need a reproduction here (code above gets close. Most likely ResolvedRecursiveType just has to indicate it's only equal to itself, to avoid the problem. I'll see if I can use pieces above to do the repro.

@newkek
Copy link
Author

newkek commented Jun 15, 2017

Ok, thanks. As I mentioned above you can copy-paste the Tree class I linked (it doesn't have any external dependency) and then just execute the code in the snippet to reproduce.

@cowtowncoder
Copy link
Member

Unfortunately I seem unable to reproduce this with trivial setup... something in there is not re-creating self-recursive type (or comparing equality). Will keep on trying, maybe I can crack this.

@cowtowncoder
Copy link
Member

Ok. And now I am actually able to produce a ResolvedRecursiveType, but without hitting StackOverflow.

So, unfortunately I will need some help reproducing the failure.

@newkek
Copy link
Author

newkek commented Jun 16, 2017

Thank you for checking.

I am confused about "And now I am actually able to produce a ResolvedRecursiveType" - did that happen when you used the Tree class I linked earlier?
This really seems to be caused by a class being both recursive, and an extension of a Map, like that Tree class.

@newkek
Copy link
Author

newkek commented Jun 16, 2017

Was able to confirm with other people - when trying to run the exact example I explained - that they run into the issue too.

@cowtowncoder
Copy link
Member

@newkek At this point I need a self-contained test. I was unable to make equality test fail for ResolvedRecursiveType, as described: this does not mean there is no problem, just that there was something in the result (perhaps call sequence, state, context) that did not give the SOE.

So: explaining an example is not of use. I need specific code.

@newkek
Copy link
Author

newkek commented Jun 16, 2017

Alright, so I pushed a self-contained test case over here: https://gist.github.com/newkek/d8998836dcdba91cc92bf5d03d46aea7

Important: After more tests, I think it seems like the issue has nothing to do with what I expected. You see that in the Tree class I included in the test class, there is a method:

public List<Tree<T>> getLeafTrees()

That method is never called, and it doesn't do anything in this code snippet. However, it turns out that if I rename the method, or remove the word "get" in the beginning of it, I do not get the SOE anymore. Do you think that this could be due to some kind of "getter inspection" mechanism in the Jackson code by any chance?

Please let me know if you manage to reproduce, and thanks for looking into it.

@cowtowncoder
Copy link
Member

cowtowncoder commented Jun 16, 2017

Much appreciated, thank you!

As to why there is very specific path that triggers it: I bet it's a specific side effect -- yes, POJO introspection will eventually discard "methods of no interest", and difference between getter (via naming convention) and other methods is such that one is not introspected further, and one is.

It is also interesting that equals() is called: this is not that often needed for type instances, but is used when determining compatibility of getters (for example when sub-class overrides things; or when matching mix-in annotations).

So all in all it is not completely surprising that difference you see exists.

@cowtowncoder cowtowncoder added this to the 2.8.10 milestone Jun 16, 2017
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