-
Notifications
You must be signed in to change notification settings - Fork 21
IllegalAccessException on final field #42
Comments
Interesting. Thank you for providing the test case, and yes, things should work functionally the same way with or without Afterburner, so this is a bug. |
Unfortunately I am not able to reproduce your problem just by using included types, simple test instance. |
Hmm, ok. Going to dig in a bit more. This is happening for REST calls in Dropwizard (0.7.1), so I'm not setting up the mapper myself. I'll let you know what I find (might be a few days) |
@smeder Ok thanks. I just mentioned oddities I observed wrt final members, since it sounds like this problem might be quite context-sensitive. For example, only occurring for child objects, or for parents, or something with both parent and children. |
Finally got around to looking into this again. I put together a very simple test case that triggers it every time for me: https://gist.github.com/smeder/485f31cdd339691ca355 In case it matters, I am running on Java 8: java version "1.8.0_05" |
Hmmh. And it passes on my local 1.7 build... |
Also passes with 1.8 locally. I run on MacOS, for what that is worth. |
Hmm, ok. I'll have to play around a bit more with my environment to see if I can isolate what is causing the different behavior. |
I've hit this. Let me see if I can abstract to a test case. |
Ok. In general, I think I'd suggest NOT trying to inject final fields for anything but "leaf" types (ones that do not refer to structured types). It appears there is some magical limit on how long Perhaps there is a way around this, but just something to keep in mind when working around issues. |
https://github.com/bimargulies/afterburner-tc is a test case for you. There's a standard reflection trick to disable final protection on non-static fields, is there not? In my test case, there's a registered constructor creator, so the problem of final should not be a problem. In short, my test case may give you two problems: 1: afterburner ignoring a mixin with a JsonCreator, and 2: the final problem. |
From the discussion I guess I don't really understand how this is supposed to work. Why do you need to mutate final fields when there is a JsonCreator annotated constructor? |
Ok, updated my test code (https://gist.github.com/smeder/485f31cdd339691ca355) and realized that it was triggering because the Resource super class has a property that is not being included in the constructor. I'm guessing that this triggers a fallback mechanism that tries to mutate the final fields? Is there any way to mark a field as read only? |
BINGO: when I cleaned up and completed my mixin constructors, it all works. I'll push the fix so thart @smeder can look. |
@cowtowncoder is there any way to imagine a module that would just have the job of logging gaps in @JsonCreator coverage? Or a way to disable poking fields so that tests without the afterburner would fail? |
@bimargulies If there is a trick, I am not aware of that. But come to think of it, core Jackson simply uses Reflection, and Afterburner itself should not even try to change that (since it can't really do it without reflection anyway). |
@smeder I may be conflating two separate issues here -- I remember problems with Except, right, when that field is mutated; although that should seemingly also occur normally without Afterburner. However: this is where I guess Afterburner could affect things -- whereas core databind ONLY uses reflection, Afterburner mixes actual calls, so perhaps it is method calls that trigger end of time during which final fields may be modified. Or something along those lines. Or. Actually. A more likely scenario is perhaps this: ability to change values of final fields is only available if Object itself is constructed using Reflection. This is what core databind does. But Afterburner instead uses regular constructor, not reflection; and after this, final fields are finalized. If above hypothesis is correct, it is quite difficult to actually prevent this problem. |
@bimargulies There is no assumption that Having said that, I am open to suggestions on adding ways to indicate that creator should be "complete", that is, existence of properties outside of creators is considered invalid. |
Hmm, how about a way to mark a property as read only instead? |
Ok, one more suggestion: if may make sense to disable auto-detection of fields completely. By default fields of any visibility (including private) may be auto-detected for purpose of setting value. But in these example cases this is not desired. So using @smeder Read-only is achieved by not having any way to set the value. This can be done by explicitly marking fields with I hope visibility change works here, since it is also something I think you actually want to do anyway, but that just has not mattered before (i.e. it has been there all along, without needing to be, and now being harmful). |
I actually want things to be visible for serialization. As far as I can tell @JsonAutoDetect applies to both serialization & deserialization, so I don't think this would work by itself. I'm basically trying to achieve asymmetric serialization & deserialization behavior. I know my immutable "struct" like classes are a bit weird as far as Java code is concerned (I don't really see the point for bean methods on these kind of classes), but it would be nice if there was a solution that didn't involve @JsonIgnore + @JsonProperty annotated getter. Seems like asymmetric use case comes up once in a while so it would be nice if @JsonIgnore had flags for indicating whether to ignore for serialization, deserialization or both. Having said all of that, I am not sure that what I am doing is actually the right approach, so the whole issue may be moot for me. |
Not necessarily. Auto-detection applies to different accessors separately, and although fields may be used for serialization and deserialization, typically they are often either not used at all (unless someone forgets to include setter or constructor argument), or are only used for deserialization. The problem with trying to add property to One thing, however, that might make sense is to have a new If this idea (on new feature) makes sense, please file it for |
I played around a bit with auto-detection. When I turn off field detection I basically have to annotate every field with @JsonProperty for things to work. It works, but isn't great. Did you mean @JsonIgnore in your second paragraph? I think the DeserializationFeature would make sense. I'm assuming that in that case the @JsonCreator annotated constructor would be used for deserialization and json fields not represented in the constructor would be treated as unknown? |
Right, it may be necessary to use As to feature, yes, it would drop usage of final fields for setting values; if setters exist, those would still be used (since finality of setter has no relation to immutability of logical property). Also, just because field 'x' is final does not necessarily mean This feature could probably be used together with, or as an alternative to, proposed "this creator is complete, no other setters should be used" addition to |
Ok, I'll file a request for the jackson-databind module. Also wanted to say that I really appreciate the time you've spent on this. |
No problem; thank you for discussing it through; I think I finally understand low level details of what is going on. FWIW, I also filed this: FasterXML/jackson-databind#563 to see if visibility handling is working the way it is intended to. |
@smeder Actually one feature I had forgotten about is So you will probably want to disable this, just in case. And finally, I will try to add a reproducible unit test here as the very first thing. |
Ok. Also, as to the original trigger, Afterburner was not verifying whether field to access is |
I'm getting the following exception:
WARN [2014-08-18 13:57:17,088] com.kosei.dropwizard.advertiser.api.JsonOrganization$Access4JacksonDeserializer: Disabling Afterburner deserialization for type class com.kosei.dropwizard.advertiser.api.JsonOrganization, field #0, due to access error (type java.lang.IllegalAccessError, message=null)
! java.lang.IllegalAccessError: null
! at com.kosei.dropwizard.advertiser.api.JsonOrganization$Access4JacksonDeserializer.objectField(com.kosei.dropwizard.advertiser.api.JsonOrganization$Access4JacksonDeserializer.java) ~[na:0.16]
! at com.fasterxml.jackson.module.afterburner.deser.BeanPropertyMutator.objectField(BeanPropertyMutator.java:142) ~[jackson-module-afterburner-2.4.2.jar:2.4.2]
! at com.fasterxml.jackson.module.afterburner.deser.SettableObjectFieldProperty.set(SettableObjectFieldProperty.java:58) [jackson-module-afterburner-2.4.2.jar:2.4.2]
! at com.fasterxml.jackson.databind.deser.impl.PropertyValue$Regular.assign(PropertyValue.java:62) [jackson-databind-2.4.2.jar:2.4.2]
! at com.fasterxml.jackson.databind.deser.impl.PropertyBasedCreator.build(PropertyBasedCreator.java:173) [jackson-databind-2.4.2.jar:2.4.2]
! at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeUsingPropertyBased(BeanDeserializer.java:398) [jackson-databind-2.4.2.jar:2.4.2]
! at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1056) [jackson-databind-2.4.2.jar:2.4.2]
! at com.fasterxml.jackson.module.afterburner.deser.SuperSonicBeanDeserializer.deserializeFromObject(SuperSonicBeanDeserializer.java:190) [jackson-module-afterburner-2.4.2.jar:2.4.2]
! at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:124) [jackson-databind-2.4.2.jar:2.4.2]
! at com.fasterxml.jackson.module.afterburner.deser.SuperSonicBeanDeserializer.deserialize(SuperSonicBeanDeserializer.java:109) [jackson-module-afterburner-2.4.2.jar:2.4.2]
! at com.fasterxml.jackson.databind.ObjectReader._bind(ObjectReader.java:1232) [jackson-databind-2.4.2.jar:2.4.2]
! at com.fasterxml.jackson.databind.ObjectReader.readValue(ObjectReader.java:676) [jackson-databind-2.4.2.jar:2.4.2]
! at com.fasterxml.jackson.jaxrs.base.ProviderBase.readFrom(ProviderBase.java:808) [jackson-jaxrs-base-2.4.2.jar:2.4.2]
when deserializing to
I am assuming that is not expected? Plain Jackson deserializes it without problems.
The text was updated successfully, but these errors were encountered: