Skip to content

Commit 03dd8cd

Browse files
committed
More reworking of #2195 implementation
1 parent c456a08 commit 03dd8cd

File tree

10 files changed

+245
-71
lines changed

10 files changed

+245
-71
lines changed

src/main/java/com/fasterxml/jackson/databind/DatabindContext.java

-10
Original file line numberDiff line numberDiff line change
@@ -206,16 +206,6 @@ public JavaType resolveSubType(JavaType baseType, String subClassName)
206206
throw invalidTypeIdException(baseType, subClassName, "Not a subtype");
207207
}
208208

209-
/**
210-
* Lookup method similar to {@link #resolveSubType} but one that also validates
211-
* that resulting subtype is valid according to the default {@link PolymorphicTypeValidator}
212-
* for the originating {@link ObjectMapper}.
213-
*
214-
* @since 2.10
215-
*/
216-
public abstract JavaType resolveAndValidateSubType(JavaType baseType, String subClass)
217-
throws JsonMappingException;
218-
219209
/**
220210
* Lookup method similar to {@link #resolveSubType} but one that also validates
221211
* that resulting subtype is valid according to given {@link PolymorphicTypeValidator}.

src/main/java/com/fasterxml/jackson/databind/DeserializationContext.java

-5
Original file line numberDiff line numberDiff line change
@@ -273,11 +273,6 @@ public TimeZone getTimeZone() {
273273
return _config.getTimeZone();
274274
}
275275

276-
@Override // since 2.10
277-
public JavaType resolveAndValidateSubType(JavaType baseType, String subClass) throws JsonMappingException {
278-
return resolveAndValidateSubType(baseType, subClass, _config.getPolymorphicTypeValidator());
279-
}
280-
281276
/*
282277
/**********************************************************
283278
/* Access to per-call state, like generic attributes (2.3+)

src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java

+3-34
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import com.fasterxml.jackson.databind.introspect.*;
2424
import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper;
2525
import com.fasterxml.jackson.databind.jsontype.*;
26+
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
2627
import com.fasterxml.jackson.databind.jsontype.impl.StdSubtypeResolver;
2728
import com.fasterxml.jackson.databind.jsontype.impl.StdTypeResolverBuilder;
2829
import com.fasterxml.jackson.databind.node.*;
@@ -305,39 +306,7 @@ public boolean useForType(JavaType t)
305306
}
306307
}
307308

308-
/**
309-
* Default {@link PolymorphicTypeValidator} used unless explicit one is constructed.
310-
* Does not do any validation, allows all subtypes. Only used for backwards-compatibility
311-
* reasons: users should not usually use such a permissive implementation but use
312-
* allow-list/criteria - based implementation.
313-
*
314-
* @since 2.10
315-
*/
316-
protected final static class LaissezFaireValidator
317-
extends PolymorphicTypeValidator
318-
{
319-
private static final long serialVersionUID = 1L;
320-
321-
public final static LaissezFaireValidator instance = new LaissezFaireValidator();
322-
323-
@Override
324-
public Validity validateBaseType(MapperConfig<?> ctxt, JavaType baseType)
325-
throws JsonMappingException {
326-
return Validity.INDETERMINATE;
327-
}
328-
329-
@Override
330-
public Validity validateSubClassName(MapperConfig<?> ctxt,
331-
JavaType baseType, String subClassName) {
332-
return Validity.INDETERMINATE;
333-
}
334-
335-
@Override
336-
public Validity validateSubType(MapperConfig<?> ctxt, JavaType baseType,
337-
JavaType subType) {
338-
return Validity.ALLOWED;
339-
}
340-
}
309+
341310

342311
/*
343312
/**********************************************************
@@ -367,7 +336,7 @@ public Validity validateSubType(MapperConfig<?> ctxt, JavaType baseType,
367336
Locale.getDefault(),
368337
null, // to indicate "use Jackson default TimeZone" (UTC since Jackson 2.7)
369338
Base64Variants.getDefaultVariant(), // 2.1
370-
LaissezFaireValidator.instance
339+
LaissezFaireSubTypeValidator.instance
371340
);
372341

373342
/*

src/main/java/com/fasterxml/jackson/databind/SerializerProvider.java

-5
Original file line numberDiff line numberDiff line change
@@ -387,11 +387,6 @@ public TimeZone getTimeZone() {
387387
return _config.getTimeZone();
388388
}
389389

390-
@Override // since 2.10
391-
public JavaType resolveAndValidateSubType(JavaType baseType, String subClass) throws JsonMappingException {
392-
return resolveAndValidateSubType(baseType, subClass, _config.getPolymorphicTypeValidator());
393-
}
394-
395390
/*
396391
/**********************************************************
397392
/* Generic attributes (2.3+)

src/main/java/com/fasterxml/jackson/databind/deser/BasicDeserializerFactory.java

+8-1
Original file line numberDiff line numberDiff line change
@@ -1700,7 +1700,14 @@ public TypeDeserializer findPropertyTypeDeserializer(DeserializationConfig confi
17001700
// but if annotations found, may need to resolve subtypes:
17011701
Collection<NamedType> subtypes = config.getSubtypeResolver().collectAndResolveSubtypesByTypeId(
17021702
config, annotated, baseType);
1703-
return b.buildTypeDeserializer(config, baseType, subtypes);
1703+
try {
1704+
return b.buildTypeDeserializer(config, baseType, subtypes);
1705+
} catch (IllegalArgumentException e0) {
1706+
InvalidDefinitionException e = InvalidDefinitionException.from((JsonParser) null,
1707+
ClassUtil.exceptionMessage(e0), baseType);
1708+
e.initCause(e0);
1709+
throw e;
1710+
}
17041711
}
17051712

17061713
/**

src/main/java/com/fasterxml/jackson/databind/jsontype/PolymorphicTypeValidator.java

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

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

101100
/**
@@ -107,14 +106,14 @@ public abstract Validity validateSubClassName(MapperConfig<?> ctxt, JavaType bas
107106
* Validator may also choose to indicate denial by throwing a {@link JsonMappingException}
108107
* (such as {@link com.fasterxml.jackson.databind.exc.InvalidTypeIdException})
109108
*
110-
* @param ctxt Context for resolution: typically will be {@code DeserializationContext}
109+
* @param config Configuration for resolution: typically will be {@code DeserializationConfig}
111110
* @param baseType Nominal base type used for polymorphic handling: subtypes MUST be instances
112111
* of this type and assignment compatibility has been verified by Jackson core
113112
* @param subType Resolved subtype to validate
114113
*
115114
* @return Determination of validity of given class name, as a subtype of given base type:
116115
* should NOT return {@code null}
117116
*/
118-
public abstract Validity validateSubType(MapperConfig<?> ctxt, JavaType baseType,
117+
public abstract Validity validateSubType(MapperConfig<?> config, JavaType baseType,
119118
JavaType subType) throws JsonMappingException;
120119
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package com.fasterxml.jackson.databind.jsontype.impl;
2+
3+
import com.fasterxml.jackson.databind.JavaType;
4+
import com.fasterxml.jackson.databind.cfg.MapperConfig;
5+
import com.fasterxml.jackson.databind.jsontype.PolymorphicTypeValidator;
6+
7+
/**
8+
* Default {@link PolymorphicTypeValidator} used unless explicit one is constructed.
9+
* Does not do any validation, allows all subtypes. Only used for backwards-compatibility
10+
* reasons: users should not usually use such a permissive implementation but use
11+
* allow-list/criteria - based implementation.
12+
*
13+
* @since 2.10
14+
*/
15+
public final class LaissezFaireSubTypeValidator
16+
extends PolymorphicTypeValidator
17+
{
18+
private static final long serialVersionUID = 1L;
19+
20+
public final static LaissezFaireSubTypeValidator instance = new LaissezFaireSubTypeValidator();
21+
22+
@Override
23+
public Validity validateBaseType(MapperConfig<?> ctxt, JavaType baseType) {
24+
return Validity.INDETERMINATE;
25+
}
26+
27+
@Override
28+
public Validity validateSubClassName(MapperConfig<?> ctxt,
29+
JavaType baseType, String subClassName) {
30+
return Validity.ALLOWED;
31+
}
32+
33+
@Override
34+
public Validity validateSubType(MapperConfig<?> ctxt, JavaType baseType,
35+
JavaType subType) {
36+
return Validity.ALLOWED;
37+
}
38+
}

src/main/java/com/fasterxml/jackson/databind/jsontype/impl/StdTypeResolverBuilder.java

+41-8
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
import com.fasterxml.jackson.databind.annotation.NoClass;
99
import com.fasterxml.jackson.databind.cfg.MapperConfig;
1010
import com.fasterxml.jackson.databind.jsontype.*;
11+
import com.fasterxml.jackson.databind.jsontype.PolymorphicTypeValidator.Validity;
12+
import com.fasterxml.jackson.databind.util.ClassUtil;
1113

1214
/**
1315
* Default {@link TypeResolverBuilder} implementation.
@@ -122,7 +124,7 @@ public TypeDeserializer buildTypeDeserializer(DeserializationConfig config,
122124
// 27-Apr-2019, tatu: Part of [databind#2195]; must first check whether any subtypes
123125
// of basetypes might be denied or allowed
124126
final PolymorphicTypeValidator subTypeValidator = verifyBaseTypeValidity(config, baseType);
125-
127+
126128
TypeIdResolver idRes = idResolver(config, baseType, subTypeValidator, subtypes, false, true);
127129

128130
JavaType defaultImpl = defineDefaultImpl(config, baseType);
@@ -280,13 +282,6 @@ protected TypeIdResolver idResolver(MapperConfig<?> config,
280282
/**********************************************************
281283
*/
282284

283-
284-
protected PolymorphicTypeValidator verifyBaseTypeValidity(MapperConfig<?> config,
285-
JavaType baseType)
286-
{
287-
return subTypeValidator(config);
288-
}
289-
290285
/**
291286
* Overridable helper method for determining actual validator to use when constructing
292287
* type serializers and type deserializers.
@@ -299,4 +294,42 @@ protected PolymorphicTypeValidator verifyBaseTypeValidity(MapperConfig<?> config
299294
public PolymorphicTypeValidator subTypeValidator(MapperConfig<?> config) {
300295
return config.getPolymorphicTypeValidator();
301296
}
297+
298+
/**
299+
* Helper method called to check that base type is valid regarding possible constraints
300+
* on basetype/subtype combinations allowed for polymorphic type handling.
301+
* Currently limits are verified for class name - based methods only.
302+
*
303+
* @since 2.10
304+
*/
305+
protected PolymorphicTypeValidator verifyBaseTypeValidity(MapperConfig<?> config,
306+
JavaType baseType)
307+
{
308+
final PolymorphicTypeValidator ptv = subTypeValidator(config);
309+
if (_idType == JsonTypeInfo.Id.CLASS || _idType == JsonTypeInfo.Id.MINIMAL_CLASS) {
310+
final Validity validity = ptv.validateBaseType(config, baseType);
311+
// If no subtypes are legal (that is, base type itself is invalid), indicate problem
312+
if (validity == Validity.DENIED) {
313+
return reportInvalidBaseType(config, baseType, ptv);
314+
}
315+
// If there's indication that any and all subtypes are fine, replace validator itself:
316+
if (validity == Validity.ALLOWED) {
317+
return LaissezFaireSubTypeValidator.instance;
318+
}
319+
// otherwise just return validator, is to be called for each distinct type
320+
}
321+
return ptv;
322+
}
323+
324+
/**
325+
* @since 2.10
326+
*/
327+
protected PolymorphicTypeValidator reportInvalidBaseType(MapperConfig<?> config,
328+
JavaType baseType, PolymorphicTypeValidator ptv)
329+
{
330+
throw new IllegalArgumentException(String.format(
331+
"Configured `PolymorphicTypeValidator` (of type %s) denied resolution of all subtypes of base type %s",
332+
ClassUtil.classNameOf(ptv), ClassUtil.classNameOf(baseType.getRawClass()))
333+
);
334+
}
302335
}

src/main/java/com/fasterxml/jackson/databind/util/ClassUtil.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -681,7 +681,8 @@ public static String classNameOf(Object inst) {
681681
if (inst == null) {
682682
return "[null]";
683683
}
684-
return nameOf(inst.getClass());
684+
Class<?> raw = (inst instanceof Class<?>) ? (Class<?>) inst : inst.getClass();
685+
return nameOf(raw);
685686
}
686687

687688
/**

0 commit comments

Comments
 (0)