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