jackson-databind: JDK 16 Illegal reflective access for `Throwable.setCause()` with `PropertyNamingStrategy.UPPER_CAMEL_CASE`
Describe the bug Mapping a json string to an instance of RuntimeException with JDK 16 (which defaults to denying illegal reflective access) while using UPPER_CAMEL_CASE property naming strategy, fails with:
Exception in thread "main" com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Failed to call `setAccess()` on Method 'setCause' due to `java.lang.reflect.InaccessibleObjectException`, problem: Unable to make final void java.lang.Throwable.setCause(java.lang.Throwable) accessible: module java.base does not "opens java.lang" to unnamed module
This manifests in parts of the AWS v1 SDK when an error is received from the AWS API endpoints. Note example report: https://github.com/FasterXML/jackson-databind/issues/2464#issuecomment-856996757
The simplified reproduce case below is based on what the failing AWS SDK code is doing internally in com.amazonaws.transform.JsonErrorUnmarshaller.unmarshall
.
Version information 2.12.3, 2.12.5, 2.13.0-rc2
To Reproduce
Execute this code without any --add-opens
params and with default --illegal-access
setting of deny.
public class Test {
public static void main(String[] args) throws Exception {
ObjectMapper mapper = new ObjectMapper();
mapper.setPropertyNamingStrategy(PropertyNamingStrategies.UPPER_CAMEL_CASE);
String jsonString = "{\"message\":\"This is my runtime exception message\"}";
JsonNode jsonContent = mapper.readTree(jsonString);
throw mapper.treeToValue(jsonContent, RuntimeException.class);
}
}
Expected behavior
It is expected that the code above would throw a RuntimeException with a message of “This is my runtime exception message”. This is what happens when working around the issue with java parameter --add-opens java.base/java.lang=ALL-UNNAMED
Additional context
Using java parameter --illegal-access=debug
results in the following additional info:
WARNING: Illegal reflective access by com.fasterxml.jackson.databind.util.ClassUtil (file:/xxxxxx/jackson-databind-2.13.0-rc2.jar) to method java.lang.Throwable.setCause(java.lang.Throwable)
at com.fasterxml.jackson.databind.util.ClassUtil.checkAndFixAccess(ClassUtil.java:994)
at com.fasterxml.jackson.databind.introspect.AnnotatedMember.fixAccess(AnnotatedMember.java:139)
at com.fasterxml.jackson.databind.deser.impl.MethodProperty.fixAccess(MethodProperty.java:95)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBuilder._fixAccess(BeanDeserializerBuilder.java:522)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBuilder.build(BeanDeserializerBuilder.java:373)
at com.fasterxml.jackson.databind.deser.BeanDeserializerFactory.buildThrowableDeserializer(BeanDeserializerFactory.java:455)
at com.fasterxml.jackson.databind.deser.BeanDeserializerFactory.createBeanDeserializer(BeanDeserializerFactory.java:112)
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:642)
at com.fasterxml.jackson.databind.ObjectMapper._findRootDeserializer(ObjectMapper.java:4751)
at com.fasterxml.jackson.databind.ObjectMapper._readValue(ObjectMapper.java:4596)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2815)
at com.fasterxml.jackson.databind.ObjectMapper.treeToValue(ObjectMapper.java:3279)
at Test.main(Test.java:13)
Note that the issue does not manifest without mapper.setPropertyNamingStrategy(PropertyNamingStrategies.UPPER_CAMEL_CASE);
About this issue
- Original URL
- State: closed
- Created 3 years ago
- Reactions: 9
- Comments: 16 (9 by maintainers)
Commits related to this issue
- Workaround for issue#3275 See my comments in https://github.com/FasterXML/jackson-databind/issues/3275 — committed to gsinghlulu/jackson-databind by gsinghlulu 2 years ago
- Fix #3275 — committed to FasterXML/jackson-databind by cowtowncoder 2 years ago
@yawkat Ok. Just didn’t see it in JDK javadocs; I guess only public (and maybe
protected
methods are included).@gsinghlulu yes, correct on both accounts. I think fix is along the right lines; will see if we could first remove
setCause()
if it exists (or replace it). I am only hesitant about looking atCause
in case other translations might miss it. But it might be good enough too, either way thank you for providing it!Ok, yes, JDK 12 added
Throwable.setCause()
apparently.This specific issue now fixed in 2.13 (for 2.13.4, then 2.14.0). But I think there is the follow-up issue of
PropertyNamingStrategy
not working forThrowable
, will file separate issue for that.@gsinghlulu ah! Ok, I should paid closer attention there. Ok I can see how things might go there… hmmh. This does seem not like the optimal way to resolve the problem, but I can see how it would actually fix the issue here. The only (?) thing that is confusing is where the upper-case
Cause
comes from – I could see it being result ofNamingStrategy
but not sure if that could yet have been applied.… but perhaps it is, as you mentioned, from
Field
that gets renamed (initCause()
won’t because we are just adding it).I think I will have another look here: thank you for providing the potential fix, as well as explaining it – and apologies for misunderstanding it first.
Looking at the original report, the issue is wrt attempts to deserialize an Exception, during which
setCause()
needs to be accessed; and for that to work, access must be forced.I am not quite sure how to proceed with this: my first instinct is to try to suppress the failure (well, very first thing is JDK17-specific test case but after that) for this specific setter. But that in turn will probably expose the problem of not being able to chain root cause exceptions on JDK 17 and later…