• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.fasterxml.jackson.databind.deser;
2 
3 import java.util.HashMap;
4 
5 import com.fasterxml.jackson.annotation.JsonFormat;
6 import com.fasterxml.jackson.databind.*;
7 import com.fasterxml.jackson.databind.deser.std.StdDelegatingDeserializer;
8 import com.fasterxml.jackson.databind.introspect.Annotated;
9 import com.fasterxml.jackson.databind.type.*;
10 import com.fasterxml.jackson.databind.util.ClassUtil;
11 import com.fasterxml.jackson.databind.util.Converter;
12 import com.fasterxml.jackson.databind.util.LRUMap;
13 
14 /**
15  * Class that defines caching layer between callers (like
16  * {@link ObjectMapper},
17  * {@link com.fasterxml.jackson.databind.DeserializationContext})
18  * and classes that construct deserializers
19  * ({@link com.fasterxml.jackson.databind.deser.DeserializerFactory}).
20  */
21 public final class DeserializerCache
22     implements java.io.Serializable // since 2.1
23 {
24     private static final long serialVersionUID = 1L;
25 
26     /*
27     /**********************************************************
28     /* Caching
29     /**********************************************************
30      */
31 
32     /**
33      * We will also cache some dynamically constructed deserializers;
34      * specifically, ones that are expensive to construct.
35      * This currently means bean, Enum and container deserializers.
36      */
37     final protected LRUMap<JavaType, JsonDeserializer<Object>> _cachedDeserializers;
38 
39     /**
40      * During deserializer construction process we may need to keep track of partially
41      * completed deserializers, to resolve cyclic dependencies. This is the
42      * map used for storing deserializers before they are fully complete.
43      */
44     final protected HashMap<JavaType, JsonDeserializer<Object>> _incompleteDeserializers
45         = new HashMap<JavaType, JsonDeserializer<Object>>(8);
46 
47     /*
48     /**********************************************************
49     /* Life-cycle
50     /**********************************************************
51      */
52 
DeserializerCache()53     public DeserializerCache() {
54         this(2000); // see [databind#1995]
55     }
56 
DeserializerCache(int maxSize)57     public DeserializerCache(int maxSize) {
58         int initial = Math.min(64, maxSize>>2);
59         _cachedDeserializers = new LRUMap<>(initial, maxSize);
60     }
61 
62     /*
63     /**********************************************************
64     /* JDK serialization handling
65     /**********************************************************
66      */
67 
writeReplace()68     Object writeReplace() {
69         // instead of making this transient, just clear it:
70         _incompleteDeserializers.clear();
71         return this;
72     }
73 
74     /*
75     /**********************************************************
76     /* Access to caching aspects
77     /**********************************************************
78      */
79 
80     /**
81      * Method that can be used to determine how many deserializers this
82      * provider is caching currently
83      * (if it does caching: default implementation does)
84      * Exact count depends on what kind of deserializers get cached;
85      * default implementation caches only dynamically constructed deserializers,
86      * but not eagerly constructed standard deserializers (which is different
87      * from how serializer provider works).
88      *<p>
89      * The main use case for this method is to allow conditional flushing of
90      * deserializer cache, if certain number of entries is reached.
91      */
cachedDeserializersCount()92     public int cachedDeserializersCount() {
93         return _cachedDeserializers.size();
94     }
95 
96     /**
97      * Method that will drop all dynamically constructed deserializers (ones that
98      * are counted as result value for {@link #cachedDeserializersCount}).
99      * This can be used to remove memory usage (in case some deserializers are
100      * only used once or so), or to force re-construction of deserializers after
101      * configuration changes for mapper than owns the provider.
102      */
flushCachedDeserializers()103     public void flushCachedDeserializers() {
104         _cachedDeserializers.clear();
105     }
106 
107     /*
108     /**********************************************************
109     /* General deserializer locating method
110     /**********************************************************
111      */
112 
113     /**
114      * Method called to get hold of a deserializer for a value of given type;
115      * or if no such deserializer can be found, a default handler (which
116      * may do a best-effort generic serialization or just simply
117      * throw an exception when invoked).
118      *<p>
119      * Note: this method is only called for value types; not for keys.
120      * Key deserializers can be accessed using {@link #findKeyDeserializer}.
121      *<p>
122      * Note also that deserializer returned is guaranteed to be resolved
123      * (if it is of type {@link ResolvableDeserializer}), but
124      * not contextualized (wrt {@link ContextualDeserializer}): caller
125      * has to handle latter if necessary.
126      *
127      * @param ctxt Deserialization context
128      * @param propertyType Declared type of the value to deserializer (obtained using
129      *   'setter' method signature and/or type annotations
130      *
131      * @throws JsonMappingException if there are fatal problems with
132      *   accessing suitable deserializer; including that of not
133      *   finding any serializer
134      */
findValueDeserializer(DeserializationContext ctxt, DeserializerFactory factory, JavaType propertyType)135     public JsonDeserializer<Object> findValueDeserializer(DeserializationContext ctxt,
136             DeserializerFactory factory, JavaType propertyType)
137         throws JsonMappingException
138     {
139         JsonDeserializer<Object> deser = _findCachedDeserializer(propertyType);
140         if (deser == null) {
141             // If not, need to request factory to construct (or recycle)
142             deser = _createAndCacheValueDeserializer(ctxt, factory, propertyType);
143             if (deser == null) {
144                 /* Should we let caller handle it? Let's have a helper method
145                  * decide it; can throw an exception, or return a valid
146                  * deserializer
147                  */
148                 deser = _handleUnknownValueDeserializer(ctxt, propertyType);
149             }
150         }
151         return deser;
152     }
153 
154     /**
155      * Method called to get hold of a deserializer to use for deserializing
156      * keys for {@link java.util.Map}.
157      *
158      * @throws JsonMappingException if there are fatal problems with
159      *   accessing suitable key deserializer; including that of not
160      *   finding any serializer
161      */
findKeyDeserializer(DeserializationContext ctxt, DeserializerFactory factory, JavaType type)162     public KeyDeserializer findKeyDeserializer(DeserializationContext ctxt,
163             DeserializerFactory factory, JavaType type)
164         throws JsonMappingException
165     {
166         KeyDeserializer kd = factory.createKeyDeserializer(ctxt, type);
167         if (kd == null) { // if none found, need to use a placeholder that'll fail
168             return _handleUnknownKeyDeserializer(ctxt, type);
169         }
170         // First: need to resolve?
171         if (kd instanceof ResolvableDeserializer) {
172             ((ResolvableDeserializer) kd).resolve(ctxt);
173         }
174         return kd;
175     }
176 
177     /**
178      * Method called to find out whether provider would be able to find
179      * a deserializer for given type, using a root reference (i.e. not
180      * through fields or membership in an array or collection)
181      */
hasValueDeserializerFor(DeserializationContext ctxt, DeserializerFactory factory, JavaType type)182     public boolean hasValueDeserializerFor(DeserializationContext ctxt,
183             DeserializerFactory factory, JavaType type)
184         throws JsonMappingException
185     {
186         /* Note: mostly copied from findValueDeserializer, except for
187          * handling of unknown types
188          */
189         JsonDeserializer<Object> deser = _findCachedDeserializer(type);
190         if (deser == null) {
191             deser = _createAndCacheValueDeserializer(ctxt, factory, type);
192         }
193         return (deser != null);
194     }
195 
196     /*
197     /**********************************************************
198     /* Helper methods that handle cache lookups
199     /**********************************************************
200      */
201 
_findCachedDeserializer(JavaType type)202     protected JsonDeserializer<Object> _findCachedDeserializer(JavaType type)
203     {
204         if (type == null) {
205             throw new IllegalArgumentException("Null JavaType passed");
206         }
207         if (_hasCustomHandlers(type)) {
208             return null;
209         }
210         return _cachedDeserializers.get(type);
211     }
212 
213     /**
214      * Method that will try to create a deserializer for given type,
215      * and resolve and cache it if necessary
216      *
217      * @param ctxt Currently active deserialization context
218      * @param type Type of property to deserialize
219      */
_createAndCacheValueDeserializer(DeserializationContext ctxt, DeserializerFactory factory, JavaType type)220     protected JsonDeserializer<Object> _createAndCacheValueDeserializer(DeserializationContext ctxt,
221             DeserializerFactory factory, JavaType type)
222         throws JsonMappingException
223     {
224         /* Only one thread to construct deserializers at any given point in time;
225          * limitations necessary to ensure that only completely initialized ones
226          * are visible and used.
227          */
228         synchronized (_incompleteDeserializers) {
229             // Ok, then: could it be that due to a race condition, deserializer can now be found?
230             JsonDeserializer<Object> deser = _findCachedDeserializer(type);
231             if (deser != null) {
232                 return deser;
233             }
234             int count = _incompleteDeserializers.size();
235             // Or perhaps being resolved right now?
236             if (count > 0) {
237                 deser = _incompleteDeserializers.get(type);
238                 if (deser != null) {
239                     return deser;
240                 }
241             }
242             // Nope: need to create and possibly cache
243             try {
244                 return _createAndCache2(ctxt, factory, type);
245             } finally {
246                 // also: any deserializers that have been created are complete by now
247                 if (count == 0 && _incompleteDeserializers.size() > 0) {
248                     _incompleteDeserializers.clear();
249                 }
250             }
251         }
252     }
253 
254     /**
255      * Method that handles actual construction (via factory) and caching (both
256      * intermediate and eventual)
257      */
_createAndCache2(DeserializationContext ctxt, DeserializerFactory factory, JavaType type)258     protected JsonDeserializer<Object> _createAndCache2(DeserializationContext ctxt,
259             DeserializerFactory factory, JavaType type)
260         throws JsonMappingException
261     {
262         JsonDeserializer<Object> deser;
263         try {
264             deser = _createDeserializer(ctxt, factory, type);
265         } catch (IllegalArgumentException iae) {
266             // We better only expose checked exceptions, since those
267             // are what caller is expected to handle
268             throw JsonMappingException.from(ctxt, ClassUtil.exceptionMessage(iae), iae);
269         }
270         if (deser == null) {
271             return null;
272         }
273         /* cache resulting deserializer? always true for "plain" BeanDeserializer
274          * (but can be re-defined for sub-classes by using @JsonCachable!)
275          */
276         // 27-Mar-2015, tatu: As per [databind#735], avoid caching types with custom value desers
277         boolean addToCache = !_hasCustomHandlers(type) && deser.isCachable();
278 
279         /* we will temporarily hold on to all created deserializers (to
280          * handle cyclic references, and possibly reuse non-cached
281          * deserializers (list, map))
282          */
283         /* 07-Jun-2010, tatu: Danger: [JACKSON-296] was caused by accidental
284          *   resolution of a reference -- couple of ways to prevent this;
285          *   either not add Lists or Maps, or clear references eagerly.
286          *   Let's actually do both; since both seem reasonable.
287          */
288         /* Need to resolve? Mostly done for bean deserializers; required for
289          * resolving cyclic references.
290          */
291         if (deser instanceof ResolvableDeserializer) {
292             _incompleteDeserializers.put(type, deser);
293             ((ResolvableDeserializer)deser).resolve(ctxt);
294             _incompleteDeserializers.remove(type);
295         }
296         if (addToCache) {
297             _cachedDeserializers.put(type, deser);
298         }
299         return deser;
300     }
301 
302     /*
303     /**********************************************************
304     /* Helper methods for actual construction of deserializers
305     /**********************************************************
306      */
307 
308     /**
309      * Method that does the heavy lifting of checking for per-type annotations,
310      * find out full type, and figure out which actual factory method
311      * to call.
312      */
313     @SuppressWarnings("unchecked")
_createDeserializer(DeserializationContext ctxt, DeserializerFactory factory, JavaType type)314     protected JsonDeserializer<Object> _createDeserializer(DeserializationContext ctxt,
315             DeserializerFactory factory, JavaType type)
316         throws JsonMappingException
317     {
318         final DeserializationConfig config = ctxt.getConfig();
319 
320         // First things first: do we need to use abstract type mapping?
321         if (type.isAbstract() || type.isMapLikeType() || type.isCollectionLikeType()) {
322             type = factory.mapAbstractType(config, type);
323         }
324         BeanDescription beanDesc = config.introspect(type);
325         // Then: does type define explicit deserializer to use, with annotation(s)?
326         JsonDeserializer<Object> deser = findDeserializerFromAnnotation(ctxt,
327                 beanDesc.getClassInfo());
328         if (deser != null) {
329             return deser;
330         }
331 
332         // If not, may have further type-modification annotations to check:
333         JavaType newType = modifyTypeByAnnotation(ctxt, beanDesc.getClassInfo(), type);
334         if (newType != type) {
335             type = newType;
336             beanDesc = config.introspect(newType);
337         }
338 
339         // We may also have a Builder type to consider...
340         Class<?> builder = beanDesc.findPOJOBuilder();
341         if (builder != null) {
342             return (JsonDeserializer<Object>) factory.createBuilderBasedDeserializer(
343             		ctxt, type, beanDesc, builder);
344         }
345 
346         // Or perhaps a Converter?
347         Converter<Object,Object> conv = beanDesc.findDeserializationConverter();
348         if (conv == null) { // nope, just construct in normal way
349             return (JsonDeserializer<Object>) _createDeserializer2(ctxt, factory, type, beanDesc);
350         }
351         // otherwise need to do bit of introspection
352         JavaType delegateType = conv.getInputType(ctxt.getTypeFactory());
353         // One more twist, as per [databind#288]; probably need to get new BeanDesc
354         if (!delegateType.hasRawClass(type.getRawClass())) {
355             beanDesc = config.introspect(delegateType);
356         }
357         return new StdDelegatingDeserializer<Object>(conv, delegateType,
358                 _createDeserializer2(ctxt, factory, delegateType, beanDesc));
359     }
360 
_createDeserializer2(DeserializationContext ctxt, DeserializerFactory factory, JavaType type, BeanDescription beanDesc)361     protected JsonDeserializer<?> _createDeserializer2(DeserializationContext ctxt,
362             DeserializerFactory factory, JavaType type, BeanDescription beanDesc)
363         throws JsonMappingException
364     {
365         final DeserializationConfig config = ctxt.getConfig();
366         // If not, let's see which factory method to use
367 
368         // 12-Feb-20202, tatu: Need to ensure that not only all Enum implementations get
369         //    there, but also `Enum` -- latter wrt [databind#2605], polymorphic usage
370         if (type.isEnumType()) {
371             return factory.createEnumDeserializer(ctxt, type, beanDesc);
372         }
373         if (type.isContainerType()) {
374             if (type.isArrayType()) {
375                 return factory.createArrayDeserializer(ctxt, (ArrayType) type, beanDesc);
376             }
377             if (type.isMapLikeType()) {
378                 // 11-Mar-2017, tatu: As per [databind#1554], also need to block
379                 //    handling as Map if overriden with "as POJO" option.
380                 // Ideally we'd determine it bit later on (to allow custom handler checks)
381                 // but that won't work for other reasons. So do it here.
382                 // (read: rewrite for 3.0)
383                 JsonFormat.Value format = beanDesc.findExpectedFormat(null);
384                 if (format.getShape() != JsonFormat.Shape.OBJECT) {
385                     MapLikeType mlt = (MapLikeType) type;
386                     if (mlt.isTrueMapType()) {
387                         return factory.createMapDeserializer(ctxt,(MapType) mlt, beanDesc);
388                     }
389                     return factory.createMapLikeDeserializer(ctxt, mlt, beanDesc);
390                 }
391             }
392             if (type.isCollectionLikeType()) {
393                 /* 03-Aug-2012, tatu: As per [databind#40], one exception is if shape
394                  *   is to be Shape.OBJECT. Ideally we'd determine it bit later on
395                  *   (to allow custom handler checks), but that won't work for other
396                  *   reasons. So do it here.
397                  */
398                 JsonFormat.Value format = beanDesc.findExpectedFormat(null);
399                 if (format.getShape() != JsonFormat.Shape.OBJECT) {
400                     CollectionLikeType clt = (CollectionLikeType) type;
401                     if (clt.isTrueCollectionType()) {
402                         return factory.createCollectionDeserializer(ctxt, (CollectionType) clt, beanDesc);
403                     }
404                     return factory.createCollectionLikeDeserializer(ctxt, clt, beanDesc);
405                 }
406             }
407         }
408         if (type.isReferenceType()) {
409             return factory.createReferenceDeserializer(ctxt, (ReferenceType) type, beanDesc);
410         }
411         if (JsonNode.class.isAssignableFrom(type.getRawClass())) {
412             return factory.createTreeDeserializer(config, type, beanDesc);
413         }
414         return factory.createBeanDeserializer(ctxt, type, beanDesc);
415     }
416 
417     /**
418      * Helper method called to check if a class or method
419      * has annotation that tells which class to use for deserialization.
420      * Returns null if no such annotation found.
421      */
findDeserializerFromAnnotation(DeserializationContext ctxt, Annotated ann)422     protected JsonDeserializer<Object> findDeserializerFromAnnotation(DeserializationContext ctxt,
423             Annotated ann)
424         throws JsonMappingException
425     {
426         Object deserDef = ctxt.getAnnotationIntrospector().findDeserializer(ann);
427         if (deserDef == null) {
428             return null;
429         }
430         JsonDeserializer<Object> deser = ctxt.deserializerInstance(ann, deserDef);
431         // One more thing however: may need to also apply a converter:
432         return findConvertingDeserializer(ctxt, ann, deser);
433     }
434 
435     /**
436      * Helper method that will check whether given annotated entity (usually class,
437      * but may also be a property accessor) indicates that a {@link Converter} is to
438      * be used; and if so, to construct and return suitable serializer for it.
439      * If not, will simply return given serializer as is.
440      */
findConvertingDeserializer(DeserializationContext ctxt, Annotated a, JsonDeserializer<Object> deser)441     protected JsonDeserializer<Object> findConvertingDeserializer(DeserializationContext ctxt,
442             Annotated a, JsonDeserializer<Object> deser)
443         throws JsonMappingException
444     {
445         Converter<Object,Object> conv = findConverter(ctxt, a);
446         if (conv == null) {
447             return deser;
448         }
449         JavaType delegateType = conv.getInputType(ctxt.getTypeFactory());
450         return (JsonDeserializer<Object>) new StdDelegatingDeserializer<Object>(conv, delegateType, deser);
451     }
452 
findConverter(DeserializationContext ctxt, Annotated a)453     protected Converter<Object,Object> findConverter(DeserializationContext ctxt,
454             Annotated a)
455         throws JsonMappingException
456     {
457         Object convDef = ctxt.getAnnotationIntrospector().findDeserializationConverter(a);
458         if (convDef == null) {
459             return null;
460         }
461         return ctxt.converterInstance(a, convDef);
462     }
463     /**
464      * Method called to see if given method has annotations that indicate
465      * a more specific type than what the argument specifies.
466      * If annotations are present, they must specify compatible Class;
467      * instance of which can be assigned using the method. This means
468      * that the Class has to be raw class of type, or its sub-class
469      * (or, implementing class if original Class instance is an interface).
470      *
471      * @param a Method or field that the type is associated with
472      * @param type Type derived from the setter argument
473      *
474      * @return Original type if no annotations are present; or a more
475      *   specific type derived from it if type annotation(s) was found
476      *
477      * @throws JsonMappingException if invalid annotation is found
478      */
modifyTypeByAnnotation(DeserializationContext ctxt, Annotated a, JavaType type)479     private JavaType modifyTypeByAnnotation(DeserializationContext ctxt,
480             Annotated a, JavaType type)
481         throws JsonMappingException
482     {
483         AnnotationIntrospector intr = ctxt.getAnnotationIntrospector();
484         if (intr == null) {
485             return type;
486         }
487 
488         // First things first: find explicitly annotated deserializer(s)
489 
490         // then key/value handlers  (annotated deserializers)?
491         if (type.isMapLikeType()) {
492             JavaType keyType = type.getKeyType();
493             // 21-Mar-2011, tatu: ... and associated deserializer too (unless already assigned)
494             //   (not 100% why or how, but this does seem to get called more than once, which
495             //   is not good: for now, let's just avoid errors)
496             if (keyType != null && keyType.getValueHandler() == null) {
497                 Object kdDef = intr.findKeyDeserializer(a);
498                 if (kdDef != null) {
499                     KeyDeserializer kd = ctxt.keyDeserializerInstance(a, kdDef);
500                     if (kd != null) {
501                         type = ((MapLikeType) type).withKeyValueHandler(kd);
502                         keyType = type.getKeyType(); // just in case it's used below
503                     }
504                 }
505             }
506         }
507         JavaType contentType = type.getContentType();
508         if (contentType != null) {
509             if (contentType.getValueHandler() == null) { // as with above, avoid resetting (which would trigger exception)
510                 Object cdDef = intr.findContentDeserializer(a);
511                 if (cdDef != null) {
512                     JsonDeserializer<?> cd = null;
513                     if (cdDef instanceof JsonDeserializer<?>) {
514                         cdDef = (JsonDeserializer<?>) cdDef;
515                     } else {
516                         Class<?> cdClass = _verifyAsClass(cdDef, "findContentDeserializer", JsonDeserializer.None.class);
517                         if (cdClass != null) {
518                             cd = ctxt.deserializerInstance(a, cdClass);
519                         }
520                     }
521                     if (cd != null) {
522                         type = type.withContentValueHandler(cd);
523                     }
524                 }
525             }
526         }
527 
528         // And after handlers, possible type refinements
529         // (note: could possibly avoid this if explicit deserializer was invoked?)
530         type = intr.refineDeserializationType(ctxt.getConfig(), a, type);
531 
532         return type;
533     }
534 
535     /*
536     /**********************************************************
537     /* Helper methods, other
538     /**********************************************************
539      */
540 
541     /**
542      * Helper method used to prevent both caching and cache lookups for structured
543      * types that have custom value handlers
544      *
545      * @since 2.8.11
546      */
_hasCustomHandlers(JavaType t)547     private boolean _hasCustomHandlers(JavaType t) {
548         if (t.isContainerType()) {
549             // First: value types may have both value and type handlers
550             JavaType ct = t.getContentType();
551             if (ct != null) {
552                 if ((ct.getValueHandler() != null) || (ct.getTypeHandler() != null)) {
553                     return true;
554                 }
555             }
556             // Second: map(-like) types may have value handler for key (but not type; keys are untyped)
557             if (t.isMapLikeType()) {
558                 JavaType kt = t.getKeyType();
559                 if (kt.getValueHandler() != null) {
560                     return true;
561                 }
562             }
563         }
564         return false;
565     }
566 
_verifyAsClass(Object src, String methodName, Class<?> noneClass)567     private Class<?> _verifyAsClass(Object src, String methodName, Class<?> noneClass)
568     {
569         if (src == null) {
570             return null;
571         }
572         if (!(src instanceof Class)) {
573             throw new IllegalStateException("AnnotationIntrospector."+methodName+"() returned value of type "+src.getClass().getName()+": expected type JsonSerializer or Class<JsonSerializer> instead");
574         }
575         Class<?> cls = (Class<?>) src;
576         if (cls == noneClass || ClassUtil.isBogusClass(cls)) {
577             return null;
578         }
579         return cls;
580     }
581 
582     /*
583     /**********************************************************
584     /* Overridable error reporting methods
585     /**********************************************************
586      */
587 
_handleUnknownValueDeserializer(DeserializationContext ctxt, JavaType type)588     protected JsonDeserializer<Object> _handleUnknownValueDeserializer(DeserializationContext ctxt, JavaType type)
589         throws JsonMappingException
590     {
591         // Let's try to figure out the reason, to give better error messages
592         Class<?> rawClass = type.getRawClass();
593         if (!ClassUtil.isConcrete(rawClass)) {
594             return ctxt.reportBadDefinition(type, "Cannot find a Value deserializer for abstract type "+type);
595         }
596         return ctxt.reportBadDefinition(type, "Cannot find a Value deserializer for type "+type);
597     }
598 
_handleUnknownKeyDeserializer(DeserializationContext ctxt, JavaType type)599     protected KeyDeserializer _handleUnknownKeyDeserializer(DeserializationContext ctxt, JavaType type)
600         throws JsonMappingException
601     {
602         return ctxt.reportBadDefinition(type, "Cannot find a (Map) Key deserializer for type "+type);
603     }
604 }
605