0

I have an application that queries EC2 Instance data using Amazon's AWS SDK for Java. The Instance is serialized to a JSON String using the Jackson wrapper class included in the AWS SDK. Later, I'm able to use the Jackson.fromJsonString(String, Class) method to deserialize the JSON String back into an EC2 Instance object.

This all works great in my application code. However, it fails every single time when run from a JUnit test within Eclipse. I'm using the exact same data, and the exact same deserialization code. When I run it from a JUnit test, though, I get the following Exception:

com.amazonaws.SdkClientException: Unable to parse Json String.
    at com.amazonaws.util.json.Jackson.fromJsonString(Jackson.java:66)
    at com.myapp.filters.test.AbstractResourceFilterCriteriaTest.testMeetsCriteria_Fail(AbstractResourceFilterCriteriaTest.java:166)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
    at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:678)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
Caused by: com.fasterxml.jackson.databind.JsonMappingException: Conflicting setter definitions for property "instanceType": com.amazonaws.services.ec2.model.Instance#setInstanceType(1 params) vs com.amazonaws.services.ec2.model.Instance#setInstanceType(1 params)
    at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCache2(DeserializerCache.java:269)
    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:461)
    at com.fasterxml.jackson.databind.ObjectMapper._findRootDeserializer(ObjectMapper.java:3838)
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3732)
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2726)
    at com.amazonaws.util.json.Jackson.fromJsonString(Jackson.java:64)
    ... 26 more
Caused by: java.lang.IllegalArgumentException: Conflicting setter definitions for property "instanceType": com.amazonaws.services.ec2.model.Instance#setInstanceType(1 params) vs com.amazonaws.services.ec2.model.Instance#setInstanceType(1 params)
    at com.fasterxml.jackson.databind.introspect.POJOPropertyBuilder.getSetter(POJOPropertyBuilder.java:300)
    at com.fasterxml.jackson.databind.deser.BeanDeserializerFactory.filterBeanProps(BeanDeserializerFactory.java:619)
    at com.fasterxml.jackson.databind.deser.BeanDeserializerFactory.addBeanProps(BeanDeserializerFactory.java:515)
    at com.fasterxml.jackson.databind.deser.BeanDeserializerFactory.buildBeanDeserializer(BeanDeserializerFactory.java:256)
    at com.fasterxml.jackson.databind.deser.BeanDeserializerFactory.createBeanDeserializer(BeanDeserializerFactory.java:169)
    at com.fasterxml.jackson.databind.deser.DeserializerCache._createDeserializer2(DeserializerCache.java:403)
    at com.fasterxml.jackson.databind.deser.DeserializerCache._createDeserializer(DeserializerCache.java:352)
    at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCache2(DeserializerCache.java:264)
    ... 33 more

Any idea what on earth could be going wrong here? Why is it that it works just fine in the main application code, but fails when run as a JUnit test? Since I don't own the Instance object (it's part of Amazon's SDK) I can't modify any Jackson annotations. Any idea how I can get around this for my unit tests?

Shadowman
  • 11,150
  • 19
  • 100
  • 198
  • 1
    Looking at the [`Instance`](http://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/services/ec2/model/Instance.html) sources there are 2 `setInstanceType` methods, one with a `String` argument and one with an `InstanceType` enum argument, which might explain the confusion. If you know which one is used, then you could try to ignore the other using a mixin (like [here](https://github.com/FasterXML/jackson-databind/issues/1251)). As to why this is happening, I can only think of a classpath difference between the 2 processes, but I can't really put a finger on it... – Morfic Sep 21 '17 at 13:44
  • @Morfic +1 [JacksonMixInAnnotations](https://github.com/FasterXML/jackson-docs/wiki/JacksonMixInAnnotations) probably can help – varren Sep 23 '17 at 08:07

1 Answers1

2

I would say you have a version issue with SDK between tests and production possibly. Looking at their source code for this class:

https://github.com/aws/aws-sdk-java/blob/master/aws-java-sdk-ec2/src/main/java/com/amazonaws/services/ec2/model/Instance.java

They appear to have an annotation for jackson on one of the two setters, specifying that's the one to use for serialization / de-serialization. This is the latest version, I didn't tool back in history to see if older version was missing this.

Jim Weaver
  • 983
  • 5
  • 15
  • I'm running both the application code and JUnit tests on my development workstation. Same JDK, same AWS SDK, same everything (as best I can tell). But, executing the same block of code as part of the application (kicking off a JSF action, for example) works, but my JUnit test fails. – Shadowman Sep 27 '17 at 13:27
  • Compare the full classpaths - If you are launching the application and unit tests both from your IDE, the IDE should show in console the java command that kicks things off, and that should have full class paths. Save that off for both and do a compare. I'd expect some differences for test dependencies like junit - but if you see differences related to Jackson or AWS that would be key. If you are maven, a common way differences creep in is if the POM file doesn't lock in a version. – Jim Weaver Sep 27 '17 at 15:07
  • I'm using Maven for dependency management. My POM files explicitly mention a version. Still no idea what could be going on here. – Shadowman Sep 27 '17 at 15:19
  • 1
    Sometimes it can be transitive dependencies that alter classpath - for example, you lock a dependency on A to version 1 exactly, but you also have a dependency on B that in turn in its POM references A, but a different or more liberal version. That can cause non-deterministic dependency libraries. I'd definitely do a file comparison on the run-time classpath for your test launch and your application launch to rule that out. If you can send me your code under test and unit test by email I'll take a look, but it might be to entangled with other code to send easily. – Jim Weaver Sep 27 '17 at 18:36
  • One other detail - looking at their git repository history, the annotation to specify to faster jackson which setter to ignore was added very recently, 9/8/2017, version 1.11.192. That may be so new that the SDK you are using doesn't have it. It was part of a commit that added a lot of jackson annotation for similar situations: https://github.com/aws/aws-sdk-java/commit/ef7a9b59afb5083a45576f8e4b76fd2c2ee9f1be – Jim Weaver Sep 27 '17 at 19:26
  • Interesting! I’ll update my version in my Pom.xml file and test again. Thanks! – Shadowman Sep 27 '17 at 19:29
  • Success!! That did it! Thanks for the advice, Jim! – Shadowman Sep 28 '17 at 14:54
  • Glad you got around it! Still not sure why behavior was different prod and unit test lol! – Jim Weaver Sep 28 '17 at 15:45