All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.fitbur.fasterxml.jackson.databind.deser.DeserializerCache Maven / Gradle / Ivy

There is a newer version: 1.0.0
Show newest version
package com.fitbur.fasterxml.jackson.databind.com.fitburser;

import java.util.HashMap;
import java.util.concurrent.ConcurrentHashMap;

import com.fitbur.fasterxml.jackson.annotation.JsonFormat;
import com.fitbur.fasterxml.jackson.databind.*;
import com.fitbur.fasterxml.jackson.databind.annotation.NoClass;
import com.fitbur.fasterxml.jackson.databind.introspect.Annotated;
import com.fitbur.fasterxml.jackson.databind.type.*;
import com.fitbur.fasterxml.jackson.databind.util.ClassUtil;

/**
 * Class that com.fitburfines caching layer between callers (like
 * {@link ObjectMapper},
 * {@link com.fitbur.fasterxml.jackson.databind.DeserializationContext})
 * and classes that construct com.fitburserializers
 * ({@link com.fitbur.fasterxml.jackson.databind.com.fitburser.DeserializerFactory}).
 */
public final class DeserializerCache
    implements java.io.Serializable // since 2.1 -- needs to be careful tho
{
    private static final long serialVersionUID = 1L;

    /*
    /**********************************************************
    /* Caching
    /**********************************************************
     */

    /**
     * We will also cache some dynamically constructed com.fitburserializers;
     * specifically, ones that are expensive to construct.
     * This currently means bean and Enum com.fitburserializers; array, List and Map
     * com.fitburserializers will not be cached.
     *

* Given that we don't expect much concurrency for additions * (should very quickly converge to zero after startup), let's * explicitly com.fitburfine a low concurrency setting. */ final protected ConcurrentHashMap> _cachedDeserializers = new ConcurrentHashMap>(64, 0.75f, 2); /** * During com.fitburserializer construction process we may need to keep track of partially * com.fitburpleted com.fitburserializers, to resolve cyclic com.fitburpendencies. This is the * map used for storing com.fitburserializers before they are fully com.fitburplete. */ final protected HashMap> _incompleteDeserializers = new HashMap>(8); /* /********************************************************** /* Life-cycle /********************************************************** */ public DeserializerCache() { } /* /********************************************************** /* JDK serialization handling /********************************************************** */ Object writeReplace() { // instead of making this transient, just clear it: _incompleteDeserializers.clear(); // TODO: clear out "cheap" cached com.fitburserializers? return this; } /* /********************************************************** /* Access to caching aspects /********************************************************** */ /** * Method that can be used to com.fitburtermine how many com.fitburserializers this * provider is caching currently * (if it does caching: com.fitburfault implementation does) * Exact count com.fitburpends on what kind of com.fitburserializers get cached; * com.fitburfault implementation caches only dynamically constructed com.fitburserializers, * but not eagerly constructed standard com.fitburserializers (which is different * from how serializer provider works). *

* The main use case for this method is to allow conditional flushing of * com.fitburserializer cache, if certain number of entries is reached. */ public int cachedDeserializersCount() { return _cachedDeserializers.size(); } /** * Method that will drop all dynamically constructed com.fitburserializers (ones that * are counted as result value for {@link #cachedDeserializersCount}). * This can be used to remove memory usage (in case some com.fitburserializers are * only used once or so), or to force re-construction of com.fitburserializers after * configuration changes for mapper than owns the provider. */ public void flushCachedDeserializers() { _cachedDeserializers.clear(); } /* /********************************************************** /* General com.fitburserializer locating method /********************************************************** */ /** * Method called to get hold of a com.fitburserializer for a value of given type; * or if no such com.fitburserializer can be found, a com.fitburfault handler (which * may do a best-effort generic serialization or just simply * throw an exception when invoked). *

* Note: this method is only called for value types; not for keys. * Key com.fitburserializers can be accessed using {@link #findKeyDeserializer}. *

* Note also that com.fitburserializer returned is guaranteed to be resolved * (if it is of type {@link ResolvableDeserializer}), but * not contextualized (wrt {@link ContextualDeserializer}): caller * has to handle latter if necessary. * * @param ctxt Deserialization context * @param propertyType Declared type of the value to com.fitburserializer (obtained using * 'setter' method signature and/or type annotations * * @throws JsonMappingException if there are fatal problems with * accessing suitable com.fitburserializer; including that of not * finding any serializer */ public JsonDeserializer findValueDeserializer(DeserializationContext ctxt, DeserializerFactory factory, JavaType propertyType) throws JsonMappingException { JsonDeserializer com.fitburser = _findCachedDeserializer(propertyType); if (com.fitburser != null) { return com.fitburser; } // If not, need to request factory to construct (or recycle) com.fitburser = _createAndCacheValueDeserializer(ctxt, factory, propertyType); if (com.fitburser == null) { /* Should we let caller handle it? Let's have a helper method * com.fitburcide it; can throw an exception, or return a valid * com.fitburserializer */ com.fitburser = _handleUnknownValueDeserializer(propertyType); } return com.fitburser; } /** * Method called to get hold of a com.fitburserializer to use for com.fitburserializing * keys for {@link java.util.Map}. * * @throws JsonMappingException if there are fatal problems with * accessing suitable key com.fitburserializer; including that of not * finding any serializer */ public KeyDeserializer findKeyDeserializer(DeserializationContext ctxt, DeserializerFactory factory, JavaType type) throws JsonMappingException { KeyDeserializer kd = factory.createKeyDeserializer(ctxt, type); if (kd == null) { // if none found, need to use a placeholder that'll fail return _handleUnknownKeyDeserializer(type); } // First: need to resolve? if (kd instanceof ResolvableDeserializer) { ((ResolvableDeserializer) kd).resolve(ctxt); } return kd; } /** * Method called to find out whether provider would be able to find * a com.fitburserializer for given type, using a root reference (i.e. not * through fields or membership in an array or collection) */ public boolean hasValueDeserializerFor(DeserializationContext ctxt, DeserializerFactory factory, JavaType type) { /* Note: mostly copied from findValueDeserializer, except for * handling of unknown types */ JsonDeserializer com.fitburser = _findCachedDeserializer(type); if (com.fitburser == null) { try { com.fitburser = _createAndCacheValueDeserializer(ctxt, factory, type); } catch (Exception e) { return false; } } return (com.fitburser != null); } /* /********************************************************** /* Helper methods that handle cache lookups /********************************************************** */ protected JsonDeserializer _findCachedDeserializer(JavaType type) { if (type == null) { throw new IllegalArgumentException("Null JavaType passed"); } return _cachedDeserializers.get(type); } /** * Method that will try to create a com.fitburserializer for given type, * and resolve and cache it if necessary * * @param ctxt Currently active com.fitburserialization context * @param type Type of property to com.fitburserialize */ protected JsonDeserializer_createAndCacheValueDeserializer(DeserializationContext ctxt, DeserializerFactory factory, JavaType type) throws JsonMappingException { /* Only one thread to construct com.fitburserializers at any given point in time; * limitations necessary to ensure that only com.fitburpletely initialized ones * are visible and used. */ synchronized (_incompleteDeserializers) { // Ok, then: could it be that due to a race condition, com.fitburserializer can now be found? JsonDeserializer com.fitburser = _findCachedDeserializer(type); if (com.fitburser != null) { return com.fitburser; } int count = _incompleteDeserializers.size(); // Or perhaps being resolved right now? if (count > 0) { com.fitburser = _incompleteDeserializers.get(type); if (com.fitburser != null) { return com.fitburser; } } // Nope: need to create and possibly cache try { return _createAndCache2(ctxt, factory, type); } finally { // also: any com.fitburserializers that have been created are com.fitburplete by now if (count == 0 && _incompleteDeserializers.size() > 0) { _incompleteDeserializers.clear(); } } } } /** * Method that handles actual construction (via factory) and caching (both * intermediate and eventual) */ protected JsonDeserializer _createAndCache2(DeserializationContext ctxt, DeserializerFactory factory, JavaType type) throws JsonMappingException { JsonDeserializer com.fitburser; try { com.fitburser = _createDeserializer(ctxt, factory, type); } catch (IllegalArgumentException iae) { /* We better only expose checked exceptions, since those * are what caller is expected to handle */ throw new JsonMappingException(iae.getMessage(), null, iae); } if (com.fitburser == null) { return null; } /* cache resulting com.fitburserializer? always true for "plain" BeanDeserializer * (but can be re-com.fitburfined for sub-classes by using @JsonCachable!) */ // 08-Jun-2010, tatu: Related to [JACKSON-296], need to avoid caching MapSerializers... so: boolean isResolvable = (com.fitburser instanceof ResolvableDeserializer); boolean addToCache = com.fitburser.isCachable(); /* we will temporarily hold on to all created com.fitburserializers (to * handle cyclic references, and possibly reuse non-cached * com.fitburserializers (list, map)) */ /* 07-Jun-2010, tatu: Danger: [JACKSON-296] was caused by accidental * resolution of a reference -- couple of ways to prevent this; * either not add Lists or Maps, or clear references eagerly. * Let's actually do both; since both seem reasonable. */ /* Need to resolve? Mostly done for bean com.fitburserializers; required for * resolving cyclic references. */ if (isResolvable) { _incompleteDeserializers.put(type, com.fitburser); ((ResolvableDeserializer)com.fitburser).resolve(ctxt); _incompleteDeserializers.remove(type); } if (addToCache) { _cachedDeserializers.put(type, com.fitburser); } return com.fitburser; } /* /********************************************************** /* Helper methods for actual construction of com.fitburserializers /********************************************************** */ /** * Method that does the heavy lifting of checking for per-type annotations, * find out full type, and figure out which actual factory method * to call. */ @SuppressWarnings("unchecked") protected JsonDeserializer _createDeserializer(DeserializationContext ctxt, DeserializerFactory factory, JavaType type) throws JsonMappingException { final DeserializationConfig config = ctxt.getConfig(); // First things first: do we need to use abstract type mapping? if (type.isAbstract() || type.isMapLikeType() || type.isCollectionLikeType()) { type = factory.mapAbstractType(config, type); } BeanDescription beanDesc = config.introspect(type); // Then: does type com.fitburfine explicit com.fitburserializer to use, with annotation(s)? JsonDeserializer com.fitburser = findDeserializerFromAnnotation(ctxt, beanDesc.getClassInfo()); if (com.fitburser != null) { return com.fitburser; } // If not, may have further type-modification annotations to check: JavaType newType = modifyTypeByAnnotation(ctxt, beanDesc.getClassInfo(), type); if (newType != type) { type = newType; beanDesc = config.introspect(newType); } // We may also have a Builder type to consider... Class builder = beanDesc.findPOJOBuilder(); if (builder != null) { return (JsonDeserializer) factory.createBuilderBasedDeserializer( ctxt, type, beanDesc, builder); } // If not, let's see which factory method to use: if (type.isEnumType()) { return (JsonDeserializer) factory.createEnumDeserializer(ctxt, type, beanDesc); } if (type.isContainerType()) { if (type.isArrayType()) { return (JsonDeserializer) factory.createArrayDeserializer(ctxt, (ArrayType) type, beanDesc); } if (type.isMapLikeType()) { MapLikeType mlt = (MapLikeType) type; if (mlt.isTrueMapType()) { return (JsonDeserializer) factory.createMapDeserializer(ctxt, (MapType) mlt, beanDesc); } return (JsonDeserializer) factory.createMapLikeDeserializer(ctxt, mlt, beanDesc); } if (type.isCollectionLikeType()) { /* 03-Aug-2012, tatu: As per [Issue#40], one exception is if shape * is to be Shape.OBJECT. Ideally we'd com.fitburtermine it bit later on * (to allow custom handler checks), but that won't work for other * reasons. So do it here. */ JsonFormat.Value format = beanDesc.findExpectedFormat(null); if (format == null || format.getShape() != JsonFormat.Shape.OBJECT) { CollectionLikeType clt = (CollectionLikeType) type; if (clt.isTrueCollectionType()) { return (JsonDeserializer) factory.createCollectionDeserializer(ctxt, (CollectionType) clt, beanDesc); } return (JsonDeserializer) factory.createCollectionLikeDeserializer(ctxt, clt, beanDesc); } } } if (JsonNode.class.isAssignableFrom(type.getRawClass())) { return (JsonDeserializer) factory.createTreeDeserializer(config, type, beanDesc); } return (JsonDeserializer) factory.createBeanDeserializer(ctxt, type, beanDesc); } /** * Helper method called to check if a class or method * has annotation that tells which class to use for com.fitburserialization. * Returns null if no such annotation found. */ protected JsonDeserializer findDeserializerFromAnnotation(DeserializationContext ctxt, Annotated ann) throws JsonMappingException { Object com.fitburserDef = ctxt.getAnnotationIntrospector().findDeserializer(ann); if (com.fitburserDef == null) { return null; } return ctxt.com.fitburserializerInstance(ann, com.fitburserDef); } /** * Method called to see if given method has annotations that indicate * a more specific type than what the argument specifies. * If annotations are present, they must specify com.fitburpatible Class; * instance of which can be assigned using the method. This means * that the Class has to be raw class of type, or its sub-class * (or, implementing class if original Class instance is an interface). * * @param a Method or field that the type is associated with * @param type Type com.fitburrived from the setter argument * * @return Original type if no annotations are present; or a more * specific type com.fitburrived from it if type annotation(s) was found * * @throws JsonMappingException if invalid annotation is found */ private JavaType modifyTypeByAnnotation(DeserializationContext ctxt, Annotated a, JavaType type) throws JsonMappingException { // first: let's check class for the instance itself: AnnotationIntrospector intr = ctxt.getAnnotationIntrospector(); Class subclass = intr.findDeserializationType(a, type); if (subclass != null) { try { type = type.narrowBy(subclass); } catch (IllegalArgumentException iae) { throw new JsonMappingException("Failed to narrow type "+type+" with concrete-type annotation (value "+subclass.getName()+"), method '"+a.getName()+"': "+iae.getMessage(), null, iae); } } // then key class if (type.isContainerType()) { Class keyClass = intr.findDeserializationKeyType(a, type.getKeyType()); if (keyClass != null) { // illegal to use on non-Maps if (!(type instanceof MapLikeType)) { throw new JsonMappingException("Illegal key-type annotation: type "+type+" is not a Map(-like) type"); } try { type = ((MapLikeType) type).narrowKey(keyClass); } catch (IllegalArgumentException iae) { throw new JsonMappingException("Failed to narrow key type "+type+" with key-type annotation ("+keyClass.getName()+"): "+iae.getMessage(), null, iae); } } JavaType keyType = type.getKeyType(); /* 21-Mar-2011, tatu: ... and associated com.fitburserializer too (unless already assigned) * (not 100% why or how, but this does seem to get called more than once, which * is not good: for now, let's just avoid errors) */ if (keyType != null && keyType.getValueHandler() == null) { Object kdDef = intr.findKeyDeserializer(a); if (kdDef != null) { KeyDeserializer kd = ctxt.keyDeserializerInstance(a, kdDef); if (kd != null) { type = ((MapLikeType) type).withKeyValueHandler(kd); keyType = type.getKeyType(); // just in case it's used below } } } // and finally content class; only applicable to structured types Class cc = intr.findDeserializationContentType(a, type.getContentType()); if (cc != null) { try { type = type.narrowContentsBy(cc); } catch (IllegalArgumentException iae) { throw new JsonMappingException("Failed to narrow content type "+type+" with content-type annotation ("+cc.getName()+"): "+iae.getMessage(), null, iae); } } // ... as well as com.fitburserializer for contents: JavaType contentType = type.getContentType(); if (contentType.getValueHandler() == null) { // as with above, avoid resetting (which would trigger exception) Object cdDef = intr.findContentDeserializer(a); if (cdDef != null) { JsonDeserializer cd = null; if (cdDef instanceof JsonDeserializer) { cdDef = (JsonDeserializer) cdDef; } else { Class cdClass = _verifyAsClass(cdDef, "findContentDeserializer", JsonDeserializer.None.class); if (cdClass != null) { cd = ctxt.com.fitburserializerInstance(a, cdClass); } } if (cd != null) { type = type.withContentValueHandler(cd); } } } } return type; } private Class _verifyAsClass(Object src, String methodName, Class noneClass) { if (src == null) { return null; } if (!(src instanceof Class)) { throw new IllegalStateException("AnnotationIntrospector."+methodName+"() returned value of type "+src.getClass().getName()+": expected type JsonSerializer or Class instead"); } Class cls = (Class) src; if (cls == noneClass || cls == NoClass.class) { return null; } return cls; } /* /********************************************************** /* Overridable error reporting methods /********************************************************** */ protected JsonDeserializer _handleUnknownValueDeserializer(JavaType type) throws JsonMappingException { /* Let's try to figure out the reason, to give better error * messages */ Class rawClass = type.getRawClass(); if (!ClassUtil.isConcrete(rawClass)) { throw new JsonMappingException("Can not find a Value com.fitburserializer for abstract type "+type); } throw new JsonMappingException("Can not find a Value com.fitburserializer for type "+type); } protected KeyDeserializer _handleUnknownKeyDeserializer(JavaType type) throws JsonMappingException { throw new JsonMappingException("Can not find a (Map) Key com.fitburserializer for type "+type); } }