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

IncompatibleClassChangeError when deserializing a class implementing an interface with default get/set implementations #223

Closed
avnersin opened this issue Oct 22, 2023 · 6 comments

Comments

@avnersin
Copy link

I'm using jackson-module-afterburner 2.15.3. with Java 17.

The following test case shows the issue:
afterburner-test.tgz

The test output is copied below.

While this looks similar to #30, I think that the fix to that older issue was perhaps not general enough. The test case mentioned in the old issue is successful but the test case I attached fails.

I compared the generated bytecode for Cat$Access4JacksonDeserializerXXX from the runtime vs. the same code manually written. I think that the issue may be that the generated bytecode is using invokeinterface:

20: invokeinterface #20, 2 // InterfaceMethod test/Cat.setInfo:(Ljava/lang/String;)V

While the bytecode for the manually written code is using invokevirtual:

22: invokevirtual #22 // Method model/Cat.setInfo:(Ljava/lang/String;)V

Test output

Running test.CatTest
{"name":"Molly","info":""}
{"name":"Molly","info":""}
Tests run: 2, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 0.292 sec <<< FAILURE!
test.CatTest.testWithAfterBurner() Time elapsed: 0.051 sec <<< FAILURE!
java.lang.IncompatibleClassChangeError: Found class model.Cat, but interface was expected
at model.Cat$Access4JacksonDeserializerdf4cb562.stringSetter(model/Cat$Access4JacksonDeserializer.java)
at com.fasterxml.jackson.module.afterburner.deser.SettableStringMethodProperty.deserializeAndSet(SettableStringMethodProperty.java:46)
at com.fasterxml.jackson.module.afterburner.deser.SuperSonicBeanDeserializer.deserialize(SuperSonicBeanDeserializer.java:159)
at com.fasterxml.jackson.databind.deser.DefaultDeserializationContext.readRootValue(DefaultDeserializationContext.java:323)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4825)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3772)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3740)
at test.CatTest.test(CatTest.java:38)
at test.CatTest.testWithAfterBurner(CatTest.java:28)

Results :

Failed tests: test.CatTest.testWithAfterBurner(): Found class model.Cat, but interface was expected

Tests run: 2, Failures: 1, Errors: 0, Skipped: 0

@cowtowncoder
Copy link
Member

First of all: thank you for reporting this, @avnersin.

Couple of immediate notes:

  1. 2.16.0-rc1 was just released: it will probably have the same issue (I don't see any relevant changes in https://github.com/FasterXML/jackson/wiki/Jackson-Release-2.16), but just in case maybe worth checking
  2. I wonder if Blackbird might be affected

@cowtowncoder
Copy link
Member

Include test case inline for easier access:

public interface Animal {
    public String getName();

    public void setName(String name);
    default public String getInfo() {
        // implement in subclasses
        return "";
    }
    default public void setInfo(String info) {
        // implement in subclasses
    }
}

public class Cat implements Animal {
    private String name;
    @Override
    public String getName() {
        return name;
    }

    @Override
    public void setName(String name) {
        this.name = name;
    }
}

public class CatTest
{
    @Test
    public void testWithoutAfterBurner() throws JsonProcessingException {
        ObjectMapper mapper = new ObjectMapper();
        test(mapper);
    }
    
    @Test
    public void testWithAfterBurner() throws JsonProcessingException {
        ObjectMapper mapper = new ObjectMapper();
        mapper.registerModule(new AfterburnerModule());

        test(mapper);
    }
    
	private void test(ObjectMapper mapper) throws JsonProcessingException, JsonMappingException {
		Cat cat = new Cat();
        cat.setName("Molly");

        String json = mapper.writeValueAsString(cat);

        Cat cat2 = mapper.readValue(json, Cat.class);
        assertEquals(cat.getName(), cat2.getName());
	}
}

@cowtowncoder
Copy link
Member

Note: existing test at

afterburner/src/test/java/com/fasterxml/jackson/module/afterburner/deser/java8/DefaultMethodsTest.java

cowtowncoder added a commit that referenced this issue Oct 24, 2023
Add test for #223: passes for Blackbird, fails for Afterburner
@cowtowncoder
Copy link
Member

First step: add failing tests (see #224): Blackbird has no problem, Afterburner has (as reported).

@cowtowncoder cowtowncoder added this to the 2.16.0 milestone Oct 24, 2023
@cowtowncoder
Copy link
Member

@avnersin It does indeed look like change for #30 was only applied in 1 out of 4 necessary places. Will fix.

cowtowncoder added a commit that referenced this issue Oct 24, 2023
…er-call

Fix #223: apply check for default (interface) method in all applicable locations
@cowtowncoder
Copy link
Member

@avnersin Thank you for reporting this -- I fixed the issue, caused by incomplete original fix.

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

No branches or pull requests

2 participants