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
53 public DeserializerCache() {
54 this(2000); // see [databind#1995]
55 }
56
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
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 */
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 */
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 */
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 */
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 */
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
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 */
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 */
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")
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
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 == null) || format.getShape() != JsonFormat.Shape.OBJECT) {
385 MapLikeType mlt = (MapLikeType) type;
386 if (mlt instanceof MapType) {
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 == null) || format.getShape() != JsonFormat.Shape.OBJECT) {
400 CollectionLikeType clt = (CollectionLikeType) type;
401 if (clt instanceof CollectionType) {
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 */
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 */
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
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 */
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 */
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
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
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
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