Skip to content

Commit

Permalink
More reworking of #2195 implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
cowtowncoder committed Apr 30, 2019
1 parent c456a08 commit 03dd8cd
Show file tree
Hide file tree
Showing 10 changed files with 245 additions and 71 deletions.
10 changes: 0 additions & 10 deletions src/main/java/com/fasterxml/jackson/databind/DatabindContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -206,16 +206,6 @@ public JavaType resolveSubType(JavaType baseType, String subClassName)
throw invalidTypeIdException(baseType, subClassName, "Not a subtype");
}

/**
* Lookup method similar to {@link #resolveSubType} but one that also validates
* that resulting subtype is valid according to the default {@link PolymorphicTypeValidator}
* for the originating {@link ObjectMapper}.
*
* @since 2.10
*/
public abstract JavaType resolveAndValidateSubType(JavaType baseType, String subClass)
throws JsonMappingException;

/**
* Lookup method similar to {@link #resolveSubType} but one that also validates
* that resulting subtype is valid according to given {@link PolymorphicTypeValidator}.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -273,11 +273,6 @@ public TimeZone getTimeZone() {
return _config.getTimeZone();
}

@Override // since 2.10
public JavaType resolveAndValidateSubType(JavaType baseType, String subClass) throws JsonMappingException {
return resolveAndValidateSubType(baseType, subClass, _config.getPolymorphicTypeValidator());
}

/*
/**********************************************************
/* Access to per-call state, like generic attributes (2.3+)
Expand Down
37 changes: 3 additions & 34 deletions src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import com.fasterxml.jackson.databind.introspect.*;
import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper;
import com.fasterxml.jackson.databind.jsontype.*;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import com.fasterxml.jackson.databind.jsontype.impl.StdSubtypeResolver;
import com.fasterxml.jackson.databind.jsontype.impl.StdTypeResolverBuilder;
import com.fasterxml.jackson.databind.node.*;
Expand Down Expand Up @@ -305,39 +306,7 @@ public boolean useForType(JavaType t)
}
}

/**
* Default {@link PolymorphicTypeValidator} used unless explicit one is constructed.
* Does not do any validation, allows all subtypes. Only used for backwards-compatibility
* reasons: users should not usually use such a permissive implementation but use
* allow-list/criteria - based implementation.
*
* @since 2.10
*/
protected final static class LaissezFaireValidator
extends PolymorphicTypeValidator
{
private static final long serialVersionUID = 1L;

public final static LaissezFaireValidator instance = new LaissezFaireValidator();

@Override
public Validity validateBaseType(MapperConfig<?> ctxt, JavaType baseType)
throws JsonMappingException {
return Validity.INDETERMINATE;
}

@Override
public Validity validateSubClassName(MapperConfig<?> ctxt,
JavaType baseType, String subClassName) {
return Validity.INDETERMINATE;
}

@Override
public Validity validateSubType(MapperConfig<?> ctxt, JavaType baseType,
JavaType subType) {
return Validity.ALLOWED;
}
}


/*
/**********************************************************
Expand Down Expand Up @@ -367,7 +336,7 @@ public Validity validateSubType(MapperConfig<?> ctxt, JavaType baseType,
Locale.getDefault(),
null, // to indicate "use Jackson default TimeZone" (UTC since Jackson 2.7)
Base64Variants.getDefaultVariant(), // 2.1
LaissezFaireValidator.instance
LaissezFaireSubTypeValidator.instance
);

/*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -387,11 +387,6 @@ public TimeZone getTimeZone() {
return _config.getTimeZone();
}

@Override // since 2.10
public JavaType resolveAndValidateSubType(JavaType baseType, String subClass) throws JsonMappingException {
return resolveAndValidateSubType(baseType, subClass, _config.getPolymorphicTypeValidator());
}

/*
/**********************************************************
/* Generic attributes (2.3+)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1700,7 +1700,14 @@ public TypeDeserializer findPropertyTypeDeserializer(DeserializationConfig confi
// but if annotations found, may need to resolve subtypes:
Collection<NamedType> subtypes = config.getSubtypeResolver().collectAndResolveSubtypesByTypeId(
config, annotated, baseType);
return b.buildTypeDeserializer(config, baseType, subtypes);
try {
return b.buildTypeDeserializer(config, baseType, subtypes);
} catch (IllegalArgumentException e0) {
InvalidDefinitionException e = InvalidDefinitionException.from((JsonParser) null,
ClassUtil.exceptionMessage(e0), baseType);
e.initCause(e0);
throw e;
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ public enum Validity {
* are known to be safe). Check can be thought of as both optimization (for latter case)
* and eager-fail (for former case) to give better feedback.
*
* @param ctxt Context for resolution: typically will be {@code DeserializationContext}
* @param config Configuration for resolution: typically will be {@code DeserializationConfig}
* @param baseType Nominal base type used for polymorphic handling: subtypes MUST be instances
* of this type and assignment compatibility is verified by Jackson core
*
Expand All @@ -71,8 +71,7 @@ public enum Validity {
* (caller will usually throw an exception); otherwise (return {@link Validity#INDETERMINATE})
* per sub-type validation calls are made for each new subclass encountered.
*/
public abstract Validity validateBaseType(MapperConfig<?> ctxt, JavaType baseType)
throws JsonMappingException;
public abstract Validity validateBaseType(MapperConfig<?> config, JavaType baseType);

/**
* Method called after intended class name for subtype has been read (and in case of minimal
Expand All @@ -86,7 +85,7 @@ public abstract Validity validateBaseType(MapperConfig<?> ctxt, JavaType baseTyp
* Validator may also choose to indicate denial by throwing a {@link JsonMappingException}
* (such as {@link com.fasterxml.jackson.databind.exc.InvalidTypeIdException})
*
* @param ctxt Context for resolution: typically will be {@code DeserializationContext}
* @param config Configuration for resolution: typically will be {@code DeserializationConfig}
* @param baseType Nominal base type used for polymorphic handling: subtypes MUST be instances
* of this type and assignment compatibility is verified by Jackson core
* @param subClassName Name of class that will be resolved to {@link java.lang.Class} if
Expand All @@ -95,7 +94,7 @@ public abstract Validity validateBaseType(MapperConfig<?> ctxt, JavaType baseTyp
* @return Determination of validity of given class name, as a subtype of given base type:
* should NOT return {@code null}
*/
public abstract Validity validateSubClassName(MapperConfig<?> ctxt, JavaType baseType,
public abstract Validity validateSubClassName(MapperConfig<?> config, JavaType baseType,
String subClassName) throws JsonMappingException;

/**
Expand All @@ -107,14 +106,14 @@ public abstract Validity validateSubClassName(MapperConfig<?> ctxt, JavaType bas
* Validator may also choose to indicate denial by throwing a {@link JsonMappingException}
* (such as {@link com.fasterxml.jackson.databind.exc.InvalidTypeIdException})
*
* @param ctxt Context for resolution: typically will be {@code DeserializationContext}
* @param config Configuration for resolution: typically will be {@code DeserializationConfig}
* @param baseType Nominal base type used for polymorphic handling: subtypes MUST be instances
* of this type and assignment compatibility has been verified by Jackson core
* @param subType Resolved subtype to validate
*
* @return Determination of validity of given class name, as a subtype of given base type:
* should NOT return {@code null}
*/
public abstract Validity validateSubType(MapperConfig<?> ctxt, JavaType baseType,
public abstract Validity validateSubType(MapperConfig<?> config, JavaType baseType,
JavaType subType) throws JsonMappingException;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.fasterxml.jackson.databind.jsontype.impl;

import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.cfg.MapperConfig;
import com.fasterxml.jackson.databind.jsontype.PolymorphicTypeValidator;

/**
* Default {@link PolymorphicTypeValidator} used unless explicit one is constructed.
* Does not do any validation, allows all subtypes. Only used for backwards-compatibility
* reasons: users should not usually use such a permissive implementation but use
* allow-list/criteria - based implementation.
*
* @since 2.10
*/
public final class LaissezFaireSubTypeValidator
extends PolymorphicTypeValidator
{
private static final long serialVersionUID = 1L;

public final static LaissezFaireSubTypeValidator instance = new LaissezFaireSubTypeValidator();

@Override
public Validity validateBaseType(MapperConfig<?> ctxt, JavaType baseType) {
return Validity.INDETERMINATE;
}

@Override
public Validity validateSubClassName(MapperConfig<?> ctxt,
JavaType baseType, String subClassName) {
return Validity.ALLOWED;
}

@Override
public Validity validateSubType(MapperConfig<?> ctxt, JavaType baseType,
JavaType subType) {
return Validity.ALLOWED;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
import com.fasterxml.jackson.databind.annotation.NoClass;
import com.fasterxml.jackson.databind.cfg.MapperConfig;
import com.fasterxml.jackson.databind.jsontype.*;
import com.fasterxml.jackson.databind.jsontype.PolymorphicTypeValidator.Validity;
import com.fasterxml.jackson.databind.util.ClassUtil;

/**
* Default {@link TypeResolverBuilder} implementation.
Expand Down Expand Up @@ -122,7 +124,7 @@ public TypeDeserializer buildTypeDeserializer(DeserializationConfig config,
// 27-Apr-2019, tatu: Part of [databind#2195]; must first check whether any subtypes
// of basetypes might be denied or allowed
final PolymorphicTypeValidator subTypeValidator = verifyBaseTypeValidity(config, baseType);

TypeIdResolver idRes = idResolver(config, baseType, subTypeValidator, subtypes, false, true);

JavaType defaultImpl = defineDefaultImpl(config, baseType);
Expand Down Expand Up @@ -280,13 +282,6 @@ protected TypeIdResolver idResolver(MapperConfig<?> config,
/**********************************************************
*/


protected PolymorphicTypeValidator verifyBaseTypeValidity(MapperConfig<?> config,
JavaType baseType)
{
return subTypeValidator(config);
}

/**
* Overridable helper method for determining actual validator to use when constructing
* type serializers and type deserializers.
Expand All @@ -299,4 +294,42 @@ protected PolymorphicTypeValidator verifyBaseTypeValidity(MapperConfig<?> config
public PolymorphicTypeValidator subTypeValidator(MapperConfig<?> config) {
return config.getPolymorphicTypeValidator();
}

/**
* Helper method called to check that base type is valid regarding possible constraints
* on basetype/subtype combinations allowed for polymorphic type handling.
* Currently limits are verified for class name - based methods only.
*
* @since 2.10
*/
protected PolymorphicTypeValidator verifyBaseTypeValidity(MapperConfig<?> config,
JavaType baseType)
{
final PolymorphicTypeValidator ptv = subTypeValidator(config);
if (_idType == JsonTypeInfo.Id.CLASS || _idType == JsonTypeInfo.Id.MINIMAL_CLASS) {
final Validity validity = ptv.validateBaseType(config, baseType);
// If no subtypes are legal (that is, base type itself is invalid), indicate problem
if (validity == Validity.DENIED) {
return reportInvalidBaseType(config, baseType, ptv);
}
// If there's indication that any and all subtypes are fine, replace validator itself:
if (validity == Validity.ALLOWED) {
return LaissezFaireSubTypeValidator.instance;
}
// otherwise just return validator, is to be called for each distinct type
}
return ptv;
}

/**
* @since 2.10
*/
protected PolymorphicTypeValidator reportInvalidBaseType(MapperConfig<?> config,
JavaType baseType, PolymorphicTypeValidator ptv)
{
throw new IllegalArgumentException(String.format(
"Configured `PolymorphicTypeValidator` (of type %s) denied resolution of all subtypes of base type %s",
ClassUtil.classNameOf(ptv), ClassUtil.classNameOf(baseType.getRawClass()))
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -681,7 +681,8 @@ public static String classNameOf(Object inst) {
if (inst == null) {
return "[null]";
}
return nameOf(inst.getClass());
Class<?> raw = (inst instanceof Class<?>) ? (Class<?>) inst : inst.getClass();
return nameOf(raw);
}

/**
Expand Down
Loading

0 comments on commit 03dd8cd

Please sign in to comment.