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

fix 3.0.0-SNAPSHOT build issues #431

Closed
pjfanning opened this issue Sep 22, 2019 · 78 comments
Closed

fix 3.0.0-SNAPSHOT build issues #431

pjfanning opened this issue Sep 22, 2019 · 78 comments
Labels
3.x Something that likely has to be done in 3.x, not 2.x

Comments

@pjfanning
Copy link
Member

Compilation fails due to major changes in jackson-databind

https://travis-ci.org/FasterXML/jackson-module-scala/jobs/588055923

@cowtowncoder
Copy link
Member

Yeah, this will require quite a bit of work, likely. Let me know if I can help: Kotlin module has some challenges too, but I did help with some changes there.

@pjfanning pjfanning added the 3.x Something that likely has to be done in 3.x, not 2.x label Sep 23, 2019
@cowtowncoder
Copy link
Member

@pjfanning One question/suggestion, more related to 2.12 (but eventually to 3.0/master too): I created this project:

https://github.com/FasterXML/jackson-integration-tests

to allow basic sanity/smoke tests; something that would run simple tests to ensure that a set (like 2.12.0-SNAPSHOT, 3.0.0-SNAPSHOT) of components in their latest publish snapshots versions still works. I have added tests for dataformats, some of datatypes (joda, guava), as well as certain combinations (Joda + XML/CSV/Properties wrt timestamp values).

I am not sure exactly how, but I wonder if there was a way to invoke some Scala mapper sanity tests from Java, built by Maven, to do simplest of validations that a scala-supporting object mapper can be constructed and used on something that exercises its functionality?
I suspect the main challenge is that of building scala types for testing; perhaps integration-tests needs to be split to either have a support package(s) of value types (Kotlin no doubt needs something similar), or even just fully separate sub-projects (making current main-level project a sub-project).

At any rate, I would really like to quickly find out if changes I am making break Scala module: I just integrated PR for

FasterXML/jackson-databind#1296

and doing refactoring for it. I will try to keep things backwards compatible for 2.12 obviously, but sometimes there are dependencies I do not see (Scala and Kotlin typically, although of course there are many 3rd party extensions beyond this). And with 3.x I will simply remove deprecated methods so there is more work anyway.

@pjfanning
Copy link
Member Author

I'm not sure - all that can be done is to copy lots of the code and tests from jackson-module-scala into jackson-integration-tests - I'd be reluctant to maintain 2 copies of the code.

I have nightly builds running jackson-module-scala on travis but recently my travis account has had a problem that doesn't let me set up new builds for github orgs that I am member of. I need to either follow up with travis or try something else like github actions.

I'm afraid master version of jackson-module-scala was well broken before I even had a chance to set up builds. I have it compiling and tests running but many are broken - many tests are probably broken for similar reasons.

@pjfanning pjfanning reopened this Jul 25, 2020
@pjfanning
Copy link
Member Author

@cowtowncoder a lot of the broken tests appear to be because the DeserializerResolvers in master do not appear to have calls made to findReferenceDeserializer - only to findBeanDeserializer.

Tests that pass in v2.12 and that fail in master have lots of calls to findReferenceDeserializer in 2.12 but none in master.

https://github.com/FasterXML/jackson-module-scala/blob/master/src/main/scala/com/fasterxml/jackson/module/scala/deser/EitherDeserializer.scala#L115

@cowtowncoder
Copy link
Member

@pjfanning Quick clarification wrt jackson-integration-tests: the idea would be a very small set of tests, similar to "hello world" style, even just one read, one write or such. To catch catastrophic cases. I wouldn't want to maintain separate sets of tests either.
One other benefit is ability to test combinations (like Scala + CSV, Scala + XML), without adding dependencies across components. Those tests would not be duplicated FWTW.
But it is up to you of course, I will mention this as something I plan to make use of and wanted to mention.

@cowtowncoder
Copy link
Member

@pjfanning On findReferenceDeserializer: that sounds odd, as if anything, 3.0 should be making more accurate calls -- although without some of fallbacks, which may be the reason for breakage.

I would guess 2 likely sources of issues:

  1. Some calls that relied on fallback (first check reference deserializer, if not found, try bean deserializer?) do not implement fallbacks any more
  2. Type refinement is somehow not working so calls are being made to wrong lookup methods

Would it be possible to test to see if calls expected to go to findReferenceDeserializer() end up on findBeanDeserializer() or vice versa? Databind itself has only one reference type (for AtomicReference) and while that is tested is not extensively tested.

@pjfanning
Copy link
Member Author

I'm not sure when I'll next have chance to spend time on this.

With reference types, wouldn't java.util.Optional be a reference type (akin to scala.Option and the master tests are failing for scala.Option)?

@cowtowncoder
Copy link
Member

Yes, java.util.Optional is a reference type for sure; refined as such by jackson-module-java8 (in 2.x), and jackson-databind itself in master.
And I think scala.Option should become one: but it is up to module to make sure that happens with TypeModifier implementation. But I think this is done by Scala module, both for 2.x and 3.0, and should also be tested that this occurs (by resolving via TypeFactory on which appropriate TypeModifier has been registered).

@pjfanning
Copy link
Member Author

In 2.12, the reference type deserializer is created in this stack - this does not happen in master, despite the same ReferenceType being in scope.

java.lang.Exception: >>>>>> EitherDeserializerResolver findReferenceDeserializer class scala.util.Either
	at com.fasterxml.jackson.module.scala.deser.EitherDeserializerResolver$.findReferenceDeserializer(EitherDeserializer.scala:121)
	at com.fasterxml.jackson.databind.deser.BasicDeserializerFactory._findCustomReferenceDeserializer(BasicDeserializerFactory.java:1976)
	at com.fasterxml.jackson.databind.deser.BasicDeserializerFactory.createReferenceDeserializer(BasicDeserializerFactory.java:1553)
	at com.fasterxml.jackson.databind.deser.DeserializerCache._createDeserializer2(DeserializerCache.java:409)
	at com.fasterxml.jackson.databind.deser.DeserializerCache._createDeserializer(DeserializerCache.java:349)
	at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCache2(DeserializerCache.java:264)
	at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCacheValueDeserializer(DeserializerCache.java:244)
	at com.fasterxml.jackson.databind.deser.DeserializerCache.findValueDeserializer(DeserializerCache.java:142)
	at com.fasterxml.jackson.databind.DeserializationContext.findNonContextualValueDeserializer(DeserializationContext.java:581)
	at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.resolve(BeanDeserializerBase.java:524)
	at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCache2(DeserializerCache.java:293)
	at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCacheValueDeserializer(DeserializerCache.java:244)
	at com.fasterxml.jackson.databind.deser.DeserializerCache.findValueDeserializer(DeserializerCache.java:142)
	at com.fasterxml.jackson.databind.DeserializationContext.findRootValueDeserializer(DeserializationContext.java:591)
	at com.fasterxml.jackson.databind.ObjectMapper._findRootDeserializer(ObjectMapper.java:4684)
	at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4545)
	at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3497)
	at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3480)

@pjfanning
Copy link
Member Author

I found what was breaking the scala Either tests on master but quite a lot more stuff to fix still.

@cowtowncoder could you look at FasterXML/jackson-module-jsonSchema#140 ?

@cowtowncoder
Copy link
Member

@pjfanning Ok good wrt Either tests. Added a note on json-schema module: at this point I have no plans to support it for 3.x, will be end-of-life'd (not much work on 2.x either).

@pjfanning
Copy link
Member Author

@cowtowncoder another set of tests that have stopped working in master relate to the scala BigDecimal and BigInteger classes which are different from the Java equivalents. The deserializer is registered as it was for Jackson 2.x but Jackson no longer seems to try to use the deserializer.

https://github.com/FasterXML/jackson-module-scala/blob/master/src/main/scala/com/fasterxml/jackson/module/scala/deser/ScalaNumberDeserializersModule.scala#L59

I added an implementation of hasDeserializerFor - which wasn't needed for Jackson 2.x - but this method is not called. There are some unit tests and when I run them, I can't find any calls being made to the ScalaNumberDeserializersModule.

Any ideas?

@cowtowncoder
Copy link
Member

Ok, first hasDeserializerFor() -- this is not used when locating deserializers but was more intended to figure out "what is POJO" by process of elimination, figuring out things that do not use BeanDeserializer. Initial use case was to be wrt security checks for polymorphic deserialization, where something that has explicit deserializer would usually be considered relatively safe compared to POJO handling which can open security holes.
So even if that was not implemented, it should not affect finding of value deserializer.

@cowtowncoder
Copy link
Member

@pjfanning Odd. Registration code looks perfectly fine... one thing to check might be to see if for some reason other findXxxDeserializer() method was called for some reason.
That seems unlikely but maybe worth checking.

Does the test result in a BeanDeserializer being created, or exception of some kind?

I assume other custom deserializers work ok, as Scala module has a few, right? I wonder if registration code differs somehow.

@pjfanning
Copy link
Member Author

pjfanning commented Aug 3, 2020

A high fraction of the custom scala deserializers are broken. I'm just working through 1 at a time.

@pjfanning
Copy link
Member Author

pjfanning commented Aug 3, 2020

In 2.12, this code finds the right deserlializer (BasicDeserializerFactory)

    protected JsonDeserializer<Object> _findCustomBeanDeserializer(JavaType type,
            DeserializationConfig config, BeanDescription beanDesc)
        throws JsonMappingException
    {
        for (Deserializers d  : _factoryConfig.deserializers()) {

But in master, the factoryConfig.deserializers() appears to be empty - same test run in 2.12 and the factoryConfig.deserializers() has loads of deserializers, including all the custom scala ones.

@pjfanning
Copy link
Member Author

It appears to be something in the ScalaObjectMapper class - a standard JsonMapper works ok in the scala tests but the ScalaObjectMapper mixin doesn't work properly

@pjfanning
Copy link
Member Author

I've fixed the issue in ScalaObjectMapper - that fixes 50 tests but still about 30 broken or yet to be rewritten.

@cowtowncoder
Copy link
Member

Well that is good progress!

@pjfanning
Copy link
Member Author

@cowtowncoder one of the issues that is happening in master branch is that deserialization is running invalid JSON in one case - {"map":{"foo":"bar"},"seq"} - the result should have "seq":[].

jackson-module-scala does not actually create the json result and is just trying the help the core jackson code by providing class metadata. There are other issues affecting this test case on the Scala module side but could you check to see why the core code produces invalid JSON?

@cowtowncoder
Copy link
Member

@pjfanning If I can figure out how to run Scala module tests on Eclipse, can try to help. But which tests in particular has this issue?

@pjfanning
Copy link
Member Author

This issue appears in com.fasterxml.jackson.module.scala.deser.ListMapTest

@cowtowncoder
Copy link
Member

Ok at least I can run sbt test from command-line now (I got a new mac mini, reinstalling things).
And I get 27 failed tests, for scala 2.13 I think; many with generator complaining but bad output state.
I'll see if I can isolate ListMapTest

@pjfanning
Copy link
Member Author

If you just run sbt - you go into interactive mode

You can run testOnly com.fasterxml.jackson.module.scala.deser.ListMapTest

@cowtowncoder
Copy link
Member

@pjfanning Ok so ScalaAnnotationsIntrospector is added after JavaAnnotationIntrospector and both probably after default JacksonAnnotationIntrospector. When chaining, calls for later introspectors are usually made although it depends on specific method in question (for some, explicit non-null/non-empty answer from higher precedence one is enough), but handling depends on method in question.

So do you know if calls to specific method are not being made, or more generally? There have been some changes to signatures of methods too (to always pass MapperConfig); but I would expect that override match is checked by compiler.
If it's for specific AnnotationIntrospector call that is not being made, I could have a look. I think introspector pair handling is also tested to some degree, although not extensively (to ensure that logic on chaining should work).

@pjfanning
Copy link
Member Author

Jackson 2 logging (with additional stacktaces) - the 2nd call never happens in Jackson 3.

So for some reason, jackson-databind neglects to get the scala class information for the MetricPath class. And then, jackson 3 is unable to deserialize any json for Metrics because it doesn't know how deserialize MetricPaths. For some reason, Jackson 3 does not properly analyse classes that are the types of object members.

java.lang.Exception: >>>> class com.fasterxml.jackson.module.scala.deser.CaseClassDeserializerTest$Metric
  | => jat com.fasterxml.jackson.module.scala.introspect.BeanIntrospector$.apply(BeanIntrospector.scala:227)
	at com.fasterxml.jackson.module.scala.introspect.ScalaAnnotationIntrospector$._descriptorFor(ScalaAnnotationIntrospectorModule.scala:154)
	at com.fasterxml.jackson.module.scala.introspect.ScalaAnnotationIntrospector$.fieldName(ScalaAnnotationIntrospectorModule.scala:165)
	at com.fasterxml.jackson.module.scala.introspect.ScalaAnnotationIntrospector$.findImplicitPropertyName(ScalaAnnotationIntrospectorModule.scala:46)
	at com.fasterxml.jackson.databind.introspect.AnnotationIntrospectorPair.findImplicitPropertyName(AnnotationIntrospectorPair.java:501)
	at com.fasterxml.jackson.databind.introspect.POJOPropertiesCollector._addFields(POJOPropertiesCollector.java:530)
	at com.fasterxml.jackson.databind.introspect.POJOPropertiesCollector.collectAll(POJOPropertiesCollector.java:421)
	at com.fasterxml.jackson.databind.introspect.POJOPropertiesCollector.getPropertyMap(POJOPropertiesCollector.java:386)
	at com.fasterxml.jackson.databind.introspect.POJOPropertiesCollector.getProperties(POJOPropertiesCollector.java:233)
	at com.fasterxml.jackson.databind.introspect.BasicBeanDescription._properties(BasicBeanDescription.java:164)
	at com.fasterxml.jackson.databind.introspect.BasicBeanDescription.findProperties(BasicBeanDescription.java:239)
	at com.fasterxml.jackson.databind.deser.BasicDeserializerFactory._findCreatorsFromProperties(BasicDeserializerFactory.java:328)
	at com.fasterxml.jackson.databind.deser.BasicDeserializerFactory._constructDefaultValueInstantiator(BasicDeserializerFactory.java:272)
	at com.fasterxml.jackson.databind.deser.BasicDeserializerFactory.findValueInstantiator(BasicDeserializerFactory.java:223)
	at com.fasterxml.jackson.databind.deser.BeanDeserializerFactory.buildBeanDeserializer(BeanDeserializerFactory.java:261)
	at com.fasterxml.jackson.databind.deser.BeanDeserializerFactory.createBeanDeserializer(BeanDeserializerFactory.java:150)
	at com.fasterxml.jackson.databind.deser.DeserializerCache._createDeserializer2(DeserializerCache.java:415)
	at com.fasterxml.jackson.databind.deser.DeserializerCache._createDeserializer(DeserializerCache.java:350)
	at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCache2(DeserializerCache.java:264)
	at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCacheValueDeserializer(DeserializerCache.java:244)
	at com.fasterxml.jackson.databind.deser.DeserializerCache.findValueDeserializer(DeserializerCache.java:142)
	at com.fasterxml.jackson.databind.DeserializationContext.findRootValueDeserializer(DeserializationContext.java:591)
	at com.fasterxml.jackson.databind.ObjectMapper._findRootDeserializer(ObjectMapper.java:4725)
	at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4595)
	at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3551)
	at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3519)
	at com.fasterxml.jackson.module.scala.deser.DeserializerTest.deserialize(DeserializerTest.scala:18)
	at com.fasterxml.jackson.module.scala.deser.DeserializerTest.deserialize$(DeserializerTest.scala:17)
	at com.fasterxml.jackson.module.scala.deser.CaseClassDeserializerTest.deserialize(CaseClassDeserializerTest.scala:61)
	at com.fasterxml.jackson.module.scala.deser.CaseClassDeserializerTest.$anonfun$new$2(CaseClassDeserializerTest.scala:72)
	at org.scalatest.OutcomeOf.outcomeOf(OutcomeOf.scala:85)
	at org.scalatest.OutcomeOf.outcomeOf$(OutcomeOf.scala:83)
	at org.scalatest.OutcomeOf$.outcomeOf(OutcomeOf.scala:104)
	at org.scalatest.Transformer.apply(Transformer.scala:22)
	at org.scalatest.Transformer.apply(Transformer.scala:20)
	at org.scalatest.flatspec.AnyFlatSpecLike$$anon$5.apply(AnyFlatSpecLike.scala:1684)
	at org.scalatest.TestSuite.withFixture(TestSuite.scala:196)
	at org.scalatest.TestSuite.withFixture$(TestSuite.scala:195)
	at org.scalatest.flatspec.AnyFlatSpec.withFixture(AnyFlatSpec.scala:1685)
	at org.scalatest.flatspec.AnyFlatSpecLike.invokeWithFixture$1(AnyFlatSpecLike.scala:1682)
	at org.scalatest.flatspec.AnyFlatSpecLike.$anonfun$runTest$1(AnyFlatSpecLike.scala:1694)
	at org.scalatest.SuperEngine.runTestImpl(Engine.scala:306)
	at org.scalatest.flatspec.AnyFlatSpecLike.runTest(AnyFlatSpecLike.scala:1694)
	at org.scalatest.flatspec.AnyFlatSpecLike.runTest$(AnyFlatSpecLike.scala:1676)
	at org.scalatest.flatspec.AnyFlatSpec.runTest(AnyFlatSpec.scala:1685)
	at org.scalatest.flatspec.AnyFlatSpecLike.$anonfun$runTests$1(AnyFlatSpecLike.scala:1752)
	at org.scalatest.SuperEngine.$anonfun$runTestsInBranch$1(Engine.scala:413)
	at scala.collection.immutable.List.foreach(List.scala:333)
	at org.scalatest.SuperEngine.traverseSubNodes$1(Engine.scala:401)
	at org.scalatest.SuperEngine.runTestsInBranch(Engine.scala:390)
	at org.scalatest.SuperEngine.$anonfun$runTestsInBranch$1(Engine.scala:427)
	at scala.collection.immutable.List.foreach(List.scala:333)
	at org.scalatest.SuperEngine.traverseSubNodes$1(Engine.scala:401)
	at org.scalatest.SuperEngine.runTestsInBranch(Engine.scala:396)
	at org.scalatest.SuperEngine.runTestsImpl(Engine.scala:475)
	at org.scalatest.flatspec.AnyFlatSpecLike.runTests(AnyFlatSpecLike.scala:1752)
	at org.scalatest.flatspec.AnyFlatSpecLike.runTests$(AnyFlatSpecLike.scala:1751)
	at org.scalatest.flatspec.AnyFlatSpec.runTests(AnyFlatSpec.scala:1685)
	at org.scalatest.Suite.run(Suite.scala:1112)
	at org.scalatest.Suite.run$(Suite.scala:1094)
	at org.scalatest.flatspec.AnyFlatSpec.org$scalatest$flatspec$AnyFlatSpecLike$$super$run(AnyFlatSpec.scala:1685)
	at org.scalatest.flatspec.AnyFlatSpecLike.$anonfun$run$1(AnyFlatSpecLike.scala:1797)
	at org.scalatest.SuperEngine.runImpl(Engine.scala:535)
	at org.scalatest.flatspec.AnyFlatSpecLike.run(AnyFlatSpecLike.scala:1797)
	at org.scalatest.flatspec.AnyFlatSpecLike.run$(AnyFlatSpecLike.scala:1795)
	at org.scalatest.flatspec.AnyFlatSpec.run(AnyFlatSpec.scala:1685)
	at org.scalatest.tools.Framework.org$scalatest$tools$Framework$$runSuite(Framework.scala:318)
	at org.scalatest.tools.Framework$ScalaTestTask.execute(Framework.scala:513)
	at sbt.TestRunner.runTest$1(TestFramework.scala:139)
	at sbt.TestRunner.run(TestFramework.scala:154)
	at sbt.TestFramework$$anon$3$$anonfun$$lessinit$greater$1.$anonfun$apply$1(TestFramework.scala:317)
	at sbt.TestFramework$.sbt$TestFramework$$withContextLoader(TestFramework.scala:277)
	at sbt.TestFramework$$anon$3$$anonfun$$lessinit$greater$1.apply(TestFramework.scala:317)
	at sbt.TestFramework$$anon$3$$anonfun$$lessinit$greater$1.apply(TestFramework.scala:317)
	at sbt.TestFunction.apply(TestFramework.scala:329)
	at sbt.Tests$.$anonfun$toTask$1(Tests.scala:422)
	at sbt.std.Transform$$anon$3.$anonfun$apply$2(Transform.scala:46)
	at sbt.std.Transform$$anon$4.work(Transform.scala:68)
	at sbt.Execute.$anonfun$submit$2(Execute.scala:282)
	at sbt.internal.util.ErrorHandling$.wideConvert(ErrorHandling.scala:23)
	at sbt.Execute.work(Execute.scala:291)
	at sbt.Execute.$anonfun$submit$1(Execute.scala:282)
	at sbt.ConcurrentRestrictions$$anon$4.$anonfun$submitValid$1(ConcurrentRestrictions.scala:265)
	at sbt.CompletionService$$anon$2.call(CompletionService.scala:64)
	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at java.lang.Thread.run(Thread.java:748)
java.lang.Exception: >>>> class com.fasterxml.jackson.module.scala.deser.CaseClassDeserializerTest$MetricPath
	at com.fasterxml.jackson.module.scala.introspect.BeanIntrospector$.apply(BeanIntrospector.scala:227)
	at com.fasterxml.jackson.module.scala.introspect.ScalaAnnotationIntrospector$._descriptorFor(ScalaAnnotationIntrospectorModule.scala:154)
	at com.fasterxml.jackson.module.scala.introspect.ScalaAnnotationIntrospector$.fieldName(ScalaAnnotationIntrospectorModule.scala:165)
	at com.fasterxml.jackson.module.scala.introspect.ScalaAnnotationIntrospector$.findImplicitPropertyName(ScalaAnnotationIntrospectorModule.scala:46)
	at com.fasterxml.jackson.databind.introspect.AnnotationIntrospectorPair.findImplicitPropertyName(AnnotationIntrospectorPair.java:501)
	at com.fasterxml.jackson.databind.introspect.POJOPropertiesCollector._addFields(POJOPropertiesCollector.java:530)
	at com.fasterxml.jackson.databind.introspect.POJOPropertiesCollector.collectAll(POJOPropertiesCollector.java:421)
	at com.fasterxml.jackson.databind.introspect.POJOPropertiesCollector.getPropertyMap(POJOPropertiesCollector.java:386)
	at com.fasterxml.jackson.databind.introspect.POJOPropertiesCollector.getProperties(POJOPropertiesCollector.java:233)
	at com.fasterxml.jackson.databind.introspect.BasicBeanDescription._properties(BasicBeanDescription.java:164)
	at com.fasterxml.jackson.databind.introspect.BasicBeanDescription.findProperties(BasicBeanDescription.java:239)
	at com.fasterxml.jackson.databind.deser.BasicDeserializerFactory._findCreatorsFromProperties(BasicDeserializerFactory.java:328)
	at com.fasterxml.jackson.databind.deser.BasicDeserializerFactory._constructDefaultValueInstantiator(BasicDeserializerFactory.java:272)
	at com.fasterxml.jackson.databind.deser.BasicDeserializerFactory.findValueInstantiator(BasicDeserializerFactory.java:223)
	at com.fasterxml.jackson.databind.deser.BeanDeserializerFactory.buildBeanDeserializer(BeanDeserializerFactory.java:261)
	at com.fasterxml.jackson.databind.deser.BeanDeserializerFactory.createBeanDeserializer(BeanDeserializerFactory.java:150)
	at com.fasterxml.jackson.databind.deser.DeserializerCache._createDeserializer2(DeserializerCache.java:415)
	at com.fasterxml.jackson.databind.deser.DeserializerCache._createDeserializer(DeserializerCache.java:350)
	at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCache2(DeserializerCache.java:264)
	at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCacheValueDeserializer(DeserializerCache.java:244)
	at com.fasterxml.jackson.databind.deser.DeserializerCache.findValueDeserializer(DeserializerCache.java:142)
	at com.fasterxml.jackson.databind.DeserializationContext.findNonContextualValueDeserializer(DeserializationContext.java:581)
	at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.resolve(BeanDeserializerBase.java:539)
	at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCache2(DeserializerCache.java:294)
	at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCacheValueDeserializer(DeserializerCache.java:244)
	at com.fasterxml.jackson.databind.deser.DeserializerCache.findValueDeserializer(DeserializerCache.java:142)
	at com.fasterxml.jackson.databind.DeserializationContext.findRootValueDeserializer(DeserializationContext.java:591)
	at com.fasterxml.jackson.databind.ObjectMapper._findRootDeserializer(ObjectMapper.java:4725)
	at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4595)
	at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3551)
	at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3519)
	at com.fasterxml.jackson.module.scala.deser.DeserializerTest.deserialize(DeserializerTest.scala:18)
	at com.fasterxml.jackson.module.scala.deser.DeserializerTest.deserialize$(DeserializerTest.scala:17)
	at com.fasterxml.jackson.module.scala.deser.CaseClassDeserializerTest.deserialize(CaseClassDeserializerTest.scala:61)
	at com.fasterxml.jackson.module.scala.deser.CaseClassDeserializerTest.$anonfun$new$2(CaseClassDeserializerTest.scala:72)
	at org.scalatest.OutcomeOf.outcomeOf(OutcomeOf.scala:85)
	at org.scalatest.OutcomeOf.outcomeOf$(OutcomeOf.scala:83)
	at org.scalatest.OutcomeOf$.outcomeOf(OutcomeOf.scala:104)
	at org.scalatest.Transformer.apply(Transformer.scala:22)
	at org.scalatest.Transformer.apply(Transformer.scala:20)
	at org.scalatest.flatspec.AnyFlatSpecLike$$anon$5.apply(AnyFlatSpecLike.scala:1684)
	at org.scalatest.TestSuite.withFixture(TestSuite.scala:196)
	at org.scalatest.TestSuite.withFixture$(TestSuite.scala:195)
	at org.scalatest.flatspec.AnyFlatSpec.withFixture(AnyFlatSpec.scala:1685)
	at org.scalatest.flatspec.AnyFlatSpecLike.invokeWithFixture$1(AnyFlatSpecLike.scala:1682)
	at org.scalatest.flatspec.AnyFlatSpecLike.$anonfun$runTest$1(AnyFlatSpecLike.scala:1694)
	at org.scalatest.SuperEngine.runTestImpl(Engine.scala:306)
	at org.scalatest.flatspec.AnyFlatSpecLike.runTest(AnyFlatSpecLike.scala:1694)
	at org.scalatest.flatspec.AnyFlatSpecLike.runTest$(AnyFlatSpecLike.scala:1676)
	at org.scalatest.flatspec.AnyFlatSpec.runTest(AnyFlatSpec.scala:1685)
	at org.scalatest.flatspec.AnyFlatSpecLike.$anonfun$runTests$1(AnyFlatSpecLike.scala:1752)
	at org.scalatest.SuperEngine.$anonfun$runTestsInBranch$1(Engine.scala:413)
	at scala.collection.immutable.List.foreach(List.scala:333)
	at org.scalatest.SuperEngine.traverseSubNodes$1(Engine.scala:401)
	at org.scalatest.SuperEngine.runTestsInBranch(Engine.scala:390)
	at org.scalatest.SuperEngine.$anonfun$runTestsInBranch$1(Engine.scala:427)
	at scala.collection.immutable.List.foreach(List.scala:333)
	at org.scalatest.SuperEngine.traverseSubNodes$1(Engine.scala:401)
	at org.scalatest.SuperEngine.runTestsInBranch(Engine.scala:396)
	at org.scalatest.SuperEngine.runTestsImpl(Engine.scala:475)
	at org.scalatest.flatspec.AnyFlatSpecLike.runTests(AnyFlatSpecLike.scala:1752)
	at org.scalatest.flatspec.AnyFlatSpecLike.runTests$(AnyFlatSpecLike.scala:1751)
	at org.scalatest.flatspec.AnyFlatSpec.runTests(AnyFlatSpec.scala:1685)
	at org.scalatest.Suite.run(Suite.scala:1112)
	at org.scalatest.Suite.run$(Suite.scala:1094)
	at org.scalatest.flatspec.AnyFlatSpec.org$scalatest$flatspec$AnyFlatSpecLike$$super$run(AnyFlatSpec.scala:1685)
	at org.scalatest.flatspec.AnyFlatSpecLike.$anonfun$run$1(AnyFlatSpecLike.scala:1797)
	at org.scalatest.SuperEngine.runImpl(Engine.scala:535)
	at org.scalatest.flatspec.AnyFlatSpecLike.run(AnyFlatSpecLike.scala:1797)
	at org.scalatest.flatspec.AnyFlatSpecLike.run$(AnyFlatSpecLike.scala:1795)
	at org.scalatest.flatspec.AnyFlatSpec.run(AnyFlatSpec.scala:1685)
	at org.scalatest.tools.Framework.org$scalatest$tools$Framework$$runSuite(Framework.scala:318)
	at org.scalatest.tools.Framework$ScalaTestTask.execute(Framework.scala:513)
	at sbt.TestRunner.runTest$1(TestFramework.scala:139)
	at sbt.TestRunner.run(TestFramework.scala:154)
	at sbt.TestFramework$$anon$3$$anonfun$$lessinit$greater$1.$anonfun$apply$1(TestFramework.scala:317)
	at sbt.TestFramework$.sbt$TestFramework$$withContextLoader(TestFramework.scala:277)
	at sbt.TestFramework$$anon$3$$anonfun$$lessinit$greater$1.apply(TestFramework.scala:317)
	at sbt.TestFramework$$anon$3$$anonfun$$lessinit$greater$1.apply(TestFramework.scala:317)
	at sbt.TestFunction.apply(TestFramework.scala:329)
	at sbt.Tests$.$anonfun$toTask$1(Tests.scala:422)
	at sbt.std.Transform$$anon$3.$anonfun$apply$2(Transform.scala:46)
	at sbt.std.Transform$$anon$4.work(Transform.scala:68)
	at sbt.Execute.$anonfun$submit$2(Execute.scala:282)
	at sbt.internal.util.ErrorHandling$.wideConvert(ErrorHandling.scala:23)
	at sbt.Execute.work(Execute.scala:291)
	at sbt.Execute.$anonfun$submit$1(Execute.scala:282)
	at sbt.ConcurrentRestrictions$$anon$4.$anonfun$submitValid$1(ConcurrentRestrictions.scala:265)
	at sbt.CompletionService$$anon$2.call(CompletionService.scala:64)
	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at java.lang.Thread.run(Thread.java:748)

@pjfanning
Copy link
Member Author

Jackson 3 seems to do value instantiation first

java.lang.Exception: >>>> class com.fasterxml.jackson.module.scala.deser.CaseClassDeserializerTest$Metric
  | => cat com.fasterxml.jackson.module.scala.introspect.BeanIntrospector$.apply(BeanIntrospector.scala:227)
	at com.fasterxml.jackson.module.scala.introspect.ScalaAnnotationIntrospector$._descriptorFor(ScalaAnnotationIntrospectorModule.scala:164)
	at com.fasterxml.jackson.module.scala.introspect.ScalaAnnotationIntrospector$.findValueInstantiator(ScalaAnnotationIntrospectorModule.scala:149)
	at com.fasterxml.jackson.databind.deser.BasicDeserializerFactory.findValueInstantiator(BasicDeserializerFactory.java:172)
	at com.fasterxml.jackson.databind.deser.BeanDeserializerFactory.buildBeanDeserializer(BeanDeserializerFactory.java:255)
	at com.fasterxml.jackson.databind.deser.BeanDeserializerFactory.createBeanDeserializer(BeanDeserializerFactory.java:150)
	at com.fasterxml.jackson.databind.deser.DeserializerCache._createDeserializer2(DeserializerCache.java:393)
	at com.fasterxml.jackson.databind.deser.DeserializerCache._createDeserializer(DeserializerCache.java:330)
	at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCache2(DeserializerCache.java:244)
	at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCacheValueDeserializer(DeserializerCache.java:225)
	at com.fasterxml.jackson.databind.deser.DeserializerCache.findValueDeserializer(DeserializerCache.java:146)
	at com.fasterxml.jackson.databind.DeserializationContext.findRootValueDeserializer(DeserializationContext.java:637)

@cowtowncoder
Copy link
Member

So the "missing" method would be findImplicitPropertyName()?

@pjfanning
Copy link
Member Author

findImplicitPropertyName is implemented in the ScalaAnnotationIntrospector and gets call correctly for Metric but not for the MetricPath class. I've been trying to step though jackson-databind but the jackson-databind code is not something I really understand. I can see the jackson-databind code does look at the properties of Metric class including the property with MetricPath type but gives up and never calls findImplicitPropertyName for the MetricPath class.

Is it possible that jackson 3 requires ScalaAnnotationIntrospector to implement more methods? - right now it is only overrides a small number of methods on NopAnnotationIntrospector

@cowtowncoder
Copy link
Member

Ok. It is possible other things might be needed. But another thing is that introspection may also be little bit asynchronous, in that order in which things go may be different.
Perhaps debug statements in POJOPropertiesCollector could show if MetricPath even gets introspected.

@pjfanning
Copy link
Member Author

pjfanning commented Feb 10, 2021

POJOPropertiesCollector never gets instantiated for MetricPath (in jackson 3). It does for Metric class.

I'm debugging code in IntelliJ - it is easy to get IntelliJ to debug the jackson-module-scala tests - it's just that I simply do not understand the code in jackson-databind, so I do not know what to look for in order to explain why jackson 3 and not jackson 2 refuses to introspect the MetricPath class.

jackson-module-scala is unusable if it cannot handle classes like Metric.

@cowtowncoder
Copy link
Member

cowtowncoder commented Feb 11, 2021

@pjfanning Is there something special about MetricPath? I could try to troubleshoot this if I can somehow recreate the type... skipping of POJOPropertiesCollector would likely be due to either deserializer being created using some other mechanism (it's taken to be some other "well-known" type, like if if it was an Enum), or if it was not yet needed.

Too bad jackson-integration-tests is just for 2.x at this point. I wonder if it'd be worth creating 2.x branches and make master be for 3.0; that way it'd be slightly easier for me to debug some Scala cases. Unfortunately I haven't been able to (in the past at least) get jackson-module-scala (or, jackson-module-kotlin for that matter) to load up successfully on Eclipse. I sometimes use IntelliJ but on that my problem is that unlike with Eclipse I cannot actually get it to debug through local snapshot version for some reason (on Eclipse if I have snapshot version open it gets properly associated so I can get matching versions to run, add breakpoints etc).

@pjfanning
Copy link
Member Author

pjfanning commented Feb 11, 2021

There is nothing special about Metric - it is as plain a class as you could possibly have in Scala.

_createAndCache2 in DeserializerCache - this code in Jackson2 works perfectly -

            if (deser instanceof ResolvableDeserializer) {
                this._incompleteDeserializers.put(type, deser);
                ((ResolvableDeserializer)deser).resolve(ctxt);
                this._incompleteDeserializers.remove(type);
            }

The resolve on BeanDeserializerBase walks the object graph - picking up all the type information for the class and the classes of its members.

The new code in jackson 3 does not even try to read the class information for the classes of the members.

@cowtowncoder
Copy link
Member

cowtowncoder commented Feb 11, 2021

@pjfanning Right I would be surprised if resolution did not proceed. What I am trying to think of is where between locating a deserialization and introspection properties things get derailed.
(delegation for Java beans works fine in that there are no failure cases there for 3.0)

What is the fail message again? Presumable a deserializer (but not functioning one) gets created or... ?

@pjfanning
Copy link
Member Author

in jackson 3 BeanDeserializerFactory addBeanProps, the code just simply fails to get the creator property for the 'path' property that had MetricPath type

@pjfanning
Copy link
Member Author

@cowtowncoder test fails with #431 (comment)

@cowtowncoder
Copy link
Member

Ok, perhaps it'd be simpler to troubleshoot direct deserialization of MetricPath type: I assume that would fail as well but perhaps show direct stack trace (as opposed to indirect via Metric).
I think I incorrectly referred to Metric earlier as well.

@cowtowncoder
Copy link
Member

Oh, wait, perhaps not. If the inner type (MetricPath, right?) is passed as creator parameter of outer type (Metric), the lack of introspection of the inner type may be simply due to failure earlier.
Until deserializer is assigned, all that is needed is type resolution (to get JavaType).

If so it would be due to missing linkage when resolving pieces of Metric and nothing to do with MetricPath -- deserializer for latter is not yet needed: it would be, at a later point, but deserializer building code is missing link between "regular" and Creator property for path (of Metric).
That is a problem on its own but not showing an issue with introspection.

At least if I am understanding the situation better now.

@pjfanning
Copy link
Member Author

MetricPath deserializes ok if it is deserialized by itself - the issue is when it is the type of a member of another class.

@cowtowncoder
Copy link
Member

Ok that makes sense. The problem seems to be that introspection does not find creator property for some reason. Not MetricPath type itself. In fact it would seem that it could as well be any other type (String) and same should occur?

At this point, would it make sense to create a new issue for this one problem. Not sure if there are other ones, but discussion here has gotten bit long and covers a few different issues, most resolved?

@pjfanning
Copy link
Member Author

I found some code in the Scala annotation classes that is not behaving as it does in Jackson 2 - let me investigate this - it could be the cause of the issue here

@pjfanning
Copy link
Member Author

@cowtowncoder a possible/probable cause is that the jackson-module-scala was modified in jackson 3 because the interface in jackson-databind changed.

ValueInstantiators findValueInstantiator(DeserializationConfig var1, BeanDescription var2, ValueInstantiator var3) has dropped the final param - the defaultValueInstantiator

The scala code now struggles to replace that default. Any suggestions as to how to get a default instantiator in jackson 3?

@cowtowncoder
Copy link
Member

cowtowncoder commented Feb 11, 2021

@pjfanning Let me see, I think this was just split in two parts or so.

Ok, yes; so, there is new modifyValueInstantiator() which is what you want to use in case you want to use the default one: so existing findValueInstantiator() is meant for full override case; and then modifyValueInstantiator() for things like delegation.

@pjfanning
Copy link
Member Author

pjfanning commented Feb 11, 2021

I have no value instantiator so can't modify it. Is there any way to create a new default instantiator that has all the creator properties calculated - that's what I get in jackson 2 - automatically but now I am left with no instantiator and need to create from scratch .

findValueInstantiator(config: DeserializationConfig, ...) -- because this method does not give access to a full DeserializationContext, it is hard to try BeanDeserializerBuilder to try to get a value instantiator from it

@cowtowncoder
Copy link
Member

Please look at signature:

    default ValueInstantiator modifyValueInstantiator(DeserializationConfig config,
            BeanDescription beanDesc, ValueInstantiator defaultInstantiator);

you are given the default instantiator UNLESS earlier call to

    public ValueInstantiator findValueInstantiator(DeserializationConfig config,
            BeanDescription beanDesc);

already returned a ValueInstantiator.

So basically earlier single call was split in two parts: one to ask for instantiator, and if none given, a default POJO instantiator is created and passed to give another chance for custom variants.

So if you want to use that default instantiator it is given and you may return it as-is, or create different one based on it (or just ignore).

@pjfanning
Copy link
Member Author

    default ValueInstantiator modifyValueInstantiator(DeserializationConfig config,
            BeanDescription beanDesc, ValueInstantiator defaultInstantiator) {
        return defaultInstantiator;
    }

I have no defaultInstantiator to pass in

@cowtowncoder
Copy link
Member

Ok now I am confused. This is a callback method of ValueInstantiators interface.
Scala module should not usually be calling it.... it is called by Jackson databind (specifically BasicDeserializerFactory). Modules would implement ValueInstantiators callback to be able to provide alternative instantiator. That handling was split from 1 to 2 methods.

If Scala module does need to call it, where is the code? Maybe I can help with it.

@pjfanning
Copy link
Member Author

In jackson 2, the perfect value instantiator is created in BasicDeserializer.findValueInstantiator -- and specifically when this is called instantiator = this._constructDefaultValueInstantiator(ctxt, beanDesc);

This instantiator is then used when the call to the old ValueInstantiators findValueInstantiator(DeserializationConfig var1, BeanDescription var2, ValueInstantiator var3) function is called

@pjfanning
Copy link
Member Author

I think I fixed it - I made the findValueInstantiator return null so now the modifyValueInstantiator is called with the instantiator created in the BasicDeserializer

@pjfanning
Copy link
Member Author

We're now down to 2 or 3 commented out tests in master that pass in jackson v2 and that fail in v3 - today's fix fixed about 5 broken tests

@cowtowncoder
Copy link
Member

@pjfanning Ok good, yes, findValueInstantiator() should return null to indicate "go ahead construct default one" and then that one will be passed for possible modification/replacement. So fix makes sense.

Getting very close to parity which is good!

@pjfanning
Copy link
Member Author

created #536 for follow on work

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
3.x Something that likely has to be done in 3.x, not 2.x
Projects
None yet
Development

No branches or pull requests

2 participants