Skip to content
This repository has been archived by the owner on Nov 5, 2019. It is now read-only.

Commit

Permalink
Backport #42 fix in 2.3.5
Browse files Browse the repository at this point in the history
  • Loading branch information
cowtowncoder committed Sep 30, 2014
1 parent da82a23 commit 628b764
Show file tree
Hide file tree
Showing 4 changed files with 181 additions and 4 deletions.
8 changes: 6 additions & 2 deletions release-notes/VERSION
Original file line number Diff line number Diff line change
@@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ protected List<OptimizedSettableBeanProperty<?>> 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())) {
Expand All @@ -134,6 +134,13 @@ protected List<OptimizedSettableBeanProperty<?>> 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) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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<OptimizedSettableBeanProperty<?>> 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<OptimizedSettableBeanProperty<?>> findOptimizableProperties(
DeserializationConfig config, PropertyMutatorCollector collector,
Iterator<SettableBeanProperty> propIterator)
{
ArrayList<OptimizedSettableBeanProperty<?>> newProps = new ArrayList<OptimizedSettableBeanProperty<?>>();

// 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);
}
}

0 comments on commit 628b764

Please sign in to comment.