diff --git a/release-notes/VERSION b/release-notes/VERSION index 6b11a57..9ca052c 100644 --- a/release-notes/VERSION +++ b/release-notes/VERSION @@ -1,12 +1,16 @@ Project: jackson-module-afterburner -Version: 2.3.4 (17-Jul-2014) +Version: 2.3.5 (xx-xxx-2014) -No changes since 2.3.3. +#42: Fails when trying to modify values of final fields ------------------------------------------------------------------------ === History: === ------------------------------------------------------------------------ +2.3.4 (17-Jul-2014) + +No changes since 2.3.3. + 2.3.3 (10-Apr-2014) #28: JsonCreator deser with single-String-argument constructor fails diff --git a/src/main/java/com/fasterxml/jackson/module/afterburner/deser/BeanPropertyMutator.java b/src/main/java/com/fasterxml/jackson/module/afterburner/deser/BeanPropertyMutator.java index 447264e..0d167d1 100644 --- a/src/main/java/com/fasterxml/jackson/module/afterburner/deser/BeanPropertyMutator.java +++ b/src/main/java/com/fasterxml/jackson/module/afterburner/deser/BeanPropertyMutator.java @@ -13,7 +13,6 @@ */ public abstract class BeanPropertyMutator { - // Intentionally not volatile for performance, worst case is we throw a few extra exceptions private boolean broken = false; diff --git a/src/main/java/com/fasterxml/jackson/module/afterburner/deser/DeserializerModifier.java b/src/main/java/com/fasterxml/jackson/module/afterburner/deser/DeserializerModifier.java index f7601ab..f129717 100644 --- a/src/main/java/com/fasterxml/jackson/module/afterburner/deser/DeserializerModifier.java +++ b/src/main/java/com/fasterxml/jackson/module/afterburner/deser/DeserializerModifier.java @@ -110,7 +110,7 @@ protected List> findOptimizableProperties( continue; } // (although, interestingly enough, can seem to access private classes...) - + // 30-Jul-2012, tatu: [Issue-6]: Needs to skip custom deserializers, if any. if (prop.hasValueDeserializer()) { if (!isDefaultDeserializer(config, prop.getValueDeserializer())) { @@ -134,6 +134,13 @@ protected List> findOptimizableProperties( } } } else if (prop instanceof FieldProperty) { // regular fields + // And as to fields, can not overwrite final fields (which may + // be overwritable via Reflection) + if (Modifier.isFinal(prop.getMember().getMember().getModifiers())) { + System.err.println("FINAL '"+prop.getName()+"' -- > NO GO!"); + continue; + } + Class type = member.getRawType(); if (type.isPrimitive()) { if (type == Integer.TYPE) { diff --git a/src/main/java/com/fasterxml/jackson/module/afterburner/deser/DeserializerModifier.java.orig b/src/main/java/com/fasterxml/jackson/module/afterburner/deser/DeserializerModifier.java.orig new file mode 100644 index 0000000..f7601ab --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/module/afterburner/deser/DeserializerModifier.java.orig @@ -0,0 +1,167 @@ +package com.fasterxml.jackson.module.afterburner.deser; + +import java.lang.reflect.Modifier; +import java.util.*; + +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.deser.*; +import com.fasterxml.jackson.databind.deser.impl.FieldProperty; +import com.fasterxml.jackson.databind.deser.impl.MethodProperty; +import com.fasterxml.jackson.databind.deser.std.StdValueInstantiator; +import com.fasterxml.jackson.databind.introspect.*; +import com.fasterxml.jackson.databind.util.ClassUtil; + +import com.fasterxml.jackson.module.afterburner.util.MyClassLoader; + +public class DeserializerModifier extends BeanDeserializerModifier +{ + /** + * Class loader to use for generated classes; if null, will try to + * use class loader of the target class. + */ + protected final MyClassLoader _classLoader; + + protected final boolean _useCustomDeserializer; + + public DeserializerModifier(ClassLoader cl, boolean useCustomDeserializer) + { + // If we were given parent class loader explicitly, use that: + _classLoader = (cl == null) ? null : new MyClassLoader(cl, false); + _useCustomDeserializer = useCustomDeserializer; + } + + /* + /********************************************************************** + /* BeanDeserializerModifier methods + /********************************************************************** + */ + + @Override + public BeanDeserializerBuilder updateBuilder(DeserializationConfig config, + BeanDescription beanDesc, BeanDeserializerBuilder builder) + { + final Class beanClass = beanDesc.getBeanClass(); + // [Issue#21]: Can't force access to sealed packages, or anything within "java." + if (!MyClassLoader.canAddClassInPackageOf(beanClass)) { + return builder; + } + /* Hmmh. Can we access stuff from private classes? + * Possibly, if we can use parent class loader. + * (should probably skip all non-public?) + */ + if (_classLoader != null) { + if (Modifier.isPrivate(beanClass.getModifiers())) { + return builder; + } + } + PropertyMutatorCollector collector = new PropertyMutatorCollector(beanClass); + List> newProps = findOptimizableProperties( + config, collector, builder.getProperties()); + // and if we found any, create mutator proxy, replace property objects + if (!newProps.isEmpty()) { + BeanPropertyMutator mutator = collector.buildMutator(_classLoader); + for (OptimizedSettableBeanProperty prop : newProps) { + builder.addOrReplaceProperty(prop.withMutator(mutator), true); + } + } + // Second thing: see if we could (re)generate Creator(s): + ValueInstantiator inst = builder.getValueInstantiator(); + /* Hmmh. Probably better to require exact default implementation + * and not sub-class; chances are sub-class uses its own + * construction anyway. + */ + if (inst.getClass() == StdValueInstantiator.class) { + // also, only override if using default creator (no-arg ctor, no-arg static factory) + if (inst.canCreateUsingDefault()) { + inst = new CreatorOptimizer(beanClass, _classLoader, (StdValueInstantiator) inst).createOptimized(); + if (inst != null) { + builder.setValueInstantiator(inst); + } + } + } + + // also: may want to replace actual BeanDeserializer as well? For this, need to replace builder + // (but only if builder is the original standard one; don't want to break other impls) + if (_useCustomDeserializer && builder.getClass() == BeanDeserializerBuilder.class) { + return new SuperSonicDeserializerBuilder(builder); + } + return builder; + } + + /* + /********************************************************************** + /* Internal methods + /********************************************************************** + */ + + protected List> findOptimizableProperties( + DeserializationConfig config, PropertyMutatorCollector collector, + Iterator propIterator) + { + ArrayList> newProps = new ArrayList>(); + + // Ok, then, find any properties for which we could generate accessors + while (propIterator.hasNext()) { + SettableBeanProperty prop = propIterator.next(); + AnnotatedMember member = prop.getMember(); + + // First: we can't access private fields or methods.... + if (Modifier.isPrivate(member.getMember().getModifiers())) { + continue; + } + // (although, interestingly enough, can seem to access private classes...) + + // 30-Jul-2012, tatu: [Issue-6]: Needs to skip custom deserializers, if any. + if (prop.hasValueDeserializer()) { + if (!isDefaultDeserializer(config, prop.getValueDeserializer())) { + continue; + } + } + + if (prop instanceof MethodProperty) { // simple setter methods + Class type = ((AnnotatedMethod) member).getRawParameterType(0); + if (type.isPrimitive()) { + if (type == Integer.TYPE) { + newProps.add(collector.addIntSetter(prop)); + } else if (type == Long.TYPE) { + newProps.add(collector.addLongSetter(prop)); + } + } else { + if (type == String.class) { + newProps.add(collector.addStringSetter(prop)); + } else { // any other Object types; we can at least call accessor + newProps.add(collector.addObjectSetter(prop)); + } + } + } else if (prop instanceof FieldProperty) { // regular fields + Class type = member.getRawType(); + if (type.isPrimitive()) { + if (type == Integer.TYPE) { + newProps.add(collector.addIntField(prop)); + } else if (type == Long.TYPE) { + newProps.add(collector.addLongField(prop)); + } + } else { + if (type == String.class) { + newProps.add(collector.addStringField(prop)); + } else { // any other Object types; we can at least call accessor + newProps.add(collector.addObjectField(prop)); + } + } + } + } + return newProps; + } + + + /** + * Helper method used to check whether given deserializer is the default + * deserializer implementation: this is necessary to avoid overriding other + * kinds of deserializers. + */ + protected boolean isDefaultDeserializer(DeserializationConfig config, + JsonDeserializer ser) + { + return ClassUtil.isJacksonStdImpl(ser); + } +}