Skip to content

Commit

Permalink
Add a way to configure DeserializerCache Jackson uses (#4101)
Browse files Browse the repository at this point in the history
  • Loading branch information
JooHyukKim authored Sep 5, 2023
1 parent c0f01a5 commit 341f8d3
Show file tree
Hide file tree
Showing 7 changed files with 389 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -161,21 +161,11 @@ public abstract class DeserializationContext
/**********************************************************
*/

protected DeserializationContext(DeserializerFactory df) {
this(df, null);
}

protected DeserializationContext(DeserializerFactory df,
DeserializerCache cache)
{
if (df == null) {
throw new NullPointerException("Cannot pass null DeserializerFactory");
}
_factory = df;
if (cache == null) {
cache = new DeserializerCache();
}
_cache = cache;
_factory = Objects.requireNonNull(df);
_cache = Objects.requireNonNull(cache);
_featureFlags = 0;
_readCapabilities = null;
_config = null;
Expand All @@ -199,6 +189,24 @@ protected DeserializationContext(DeserializationContext src,
_attributes = src._attributes;
}

/**
* @since 2.16
*/
protected DeserializationContext(DeserializationContext src,
DeserializerCache cache)
{
_cache = cache;
_factory = src._factory;

_config = src._config;
_featureFlags = src._featureFlags;
_readCapabilities = src._readCapabilities;
_view = src._view;
_parser = src._parser;
_injectableValues = src._injectableValues;
_attributes = src._attributes;
}

/**
* Constructor used for creating actual per-call instances.
*/
Expand Down Expand Up @@ -243,17 +251,19 @@ protected DeserializationContext(DeserializationContext src,
}

/**
* Copy-constructor for use with <code>copy()</code> by {@link ObjectMapper#copy()}
* Copy-constructor for use with <code>copy()</code> by {@link ObjectMapper#copy()}.
* Only called on blueprint objects.
*/
protected DeserializationContext(DeserializationContext src) {
_cache = new DeserializerCache();
_cache = src._cache.emptyCopy();
_factory = src._factory;

_config = src._config;
_featureFlags = src._featureFlags;
_readCapabilities = src._readCapabilities;
_view = src._view;
_injectableValues = null;
_injectableValues = src._injectableValues;
_attributes = null;
}

/*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2282,6 +2282,7 @@ public ObjectMapper setCacheProvider(CacheProvider cacheProvider) {
_assertNotNull("cacheProvider", cacheProvider);
_deserializationConfig = _deserializationConfig.with(cacheProvider);
_serializationConfig = _serializationConfig.with(cacheProvider);
_deserializationContext = _deserializationContext.withCaches(cacheProvider);
return this;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
package com.fasterxml.jackson.databind.cfg;

import com.fasterxml.jackson.databind.DeserializationConfig;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.deser.DeserializerCache;
import com.fasterxml.jackson.databind.util.LookupCache;

/**
* Interface that defines API Jackson uses for constructing various internal
* caches. This allows configuring custom caches and cache configurations.
Expand All @@ -11,5 +17,11 @@
public interface CacheProvider
extends java.io.Serializable
{
// !!! TODO: add methods
/**
* Method to provide a {@link LookupCache} instance for constructing {@link DeserializerCache}.
*
* @return {@link LookupCache} instance for constructing {@link DeserializerCache}.
*/
LookupCache<JavaType, JsonDeserializer<Object>> forDeserializerCache(DeserializationConfig config);

}
Original file line number Diff line number Diff line change
@@ -1,19 +1,142 @@
package com.fasterxml.jackson.databind.cfg;

import com.fasterxml.jackson.databind.DeserializationConfig;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.deser.DeserializerCache;
import com.fasterxml.jackson.databind.util.LRUMap;
import com.fasterxml.jackson.databind.util.LookupCache;

/**
* Default implementation of {@link CacheProvider}.
*
* The default implementation of {@link CacheProvider}.
* Configuration is builder-based via {@link DefaultCacheProvider.Builder}.
* <p>
* Users can either use this class or create their own {@link CacheProvider} implementation.
*
* @since 2.16
*/
public class DefaultCacheProvider
implements CacheProvider
{
private static final long serialVersionUID = 1L;

public static DefaultCacheProvider defaultInstance() {
return new DefaultCacheProvider();
}
private final static DefaultCacheProvider DEFAULT
= new DefaultCacheProvider(DeserializerCache.DEFAULT_MAX_CACHE_SIZE);

/**
* Maximum size of the {@link LookupCache} instance constructed by {@link #forDeserializerCache(DeserializationConfig)}.
*
* @see Builder#maxDeserializerCacheSize(int)
*/
protected final int _maxDeserializerCacheSize;

// To implement!
/*
/**********************************************************************
/* Life cycle
/**********************************************************************
*/

protected DefaultCacheProvider(int deserializerCache)
{
_maxDeserializerCacheSize = deserializerCache;
}

/*
/**********************************************************************
/* Defaults
/**********************************************************************
*/

/**
* @return Default {@link DefaultCacheProvider} instance using default configuration values.
*/
public static CacheProvider defaultInstance() {
return DEFAULT;
}

/*
/**********************************************************
/* API implementation
/**********************************************************
*/

/**
* Method to provide a {@link LookupCache} instance for constructing {@link DeserializerCache}.
* Implementation should match {@link DeserializerCache#DeserializerCache(int)}.
*
* @return {@link LookupCache} instance for constructing {@link DeserializerCache}.
*/
@Override
public LookupCache<JavaType, JsonDeserializer<Object>> forDeserializerCache(DeserializationConfig config) {
return _buildCache(_maxDeserializerCacheSize);
}

/*
/**********************************************************
/* Overridable factory methods
/**********************************************************
*/

protected <K,V> LookupCache<K,V> _buildCache(int maxSize)
{
// Use 1/4 of maximum size (but at most 64) for initial size
final int initialSize = Math.min(64, maxSize >> 2);
return new LRUMap<>(initialSize, maxSize);
}

/*
/**********************************************************
/* Builder Config
/**********************************************************
*/

/**
* @return {@link Builder} instance for configuration.
*/
public static DefaultCacheProvider.Builder builder() {
return new Builder();
}

/**
* Builder offering fluent factory methods to configure {@link DefaultCacheProvider}, keeping it immutable.
*/
public static class Builder {

/**
* Maximum Size of the {@link LookupCache} instance created by {@link #forDeserializerCache(DeserializationConfig)}.
* Corresponds to {@link DefaultCacheProvider#_maxDeserializerCacheSize}.
*/
private int _maxDeserializerCacheSize;

Builder() { }

/**
* Define the maximum size of the {@link LookupCache} instance constructed by {@link #forDeserializerCache(DeserializationConfig)}.
* The cache is instantiated as:
* <pre>
* return new LRUMap<>(Math.min(64, maxSize >> 2), maxSize);
* </pre>
*
* @param maxDeserializerCacheSize Size for the {@link LookupCache} to use within {@link DeserializerCache}
* @return this builder
* @throws IllegalArgumentException if {@code maxDeserializerCacheSize} is negative
* @since 2.16
*/
public Builder maxDeserializerCacheSize(int maxDeserializerCacheSize) {
if (maxDeserializerCacheSize < 0) {
throw new IllegalArgumentException("Cannot set maxDeserializerCacheSize to a negative value");
}
_maxDeserializerCacheSize = maxDeserializerCacheSize;
return this;
}

/**
* Constructs a {@link DefaultCacheProvider} with the provided configuration values, using defaults where not specified.
*
* @return A {@link DefaultCacheProvider} instance with the specified configuration
*/
public DefaultCacheProvider build() {
return new DefaultCacheProvider(_maxDeserializerCacheSize);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.cfg.CacheProvider;
import com.fasterxml.jackson.databind.cfg.HandlerInstantiator;
import com.fasterxml.jackson.databind.deser.impl.ReadableObjectId;
import com.fasterxml.jackson.databind.deser.impl.ReadableObjectId.Referring;
Expand Down Expand Up @@ -62,6 +63,16 @@ protected DefaultDeserializationContext(DefaultDeserializationContext src,
}

/**
* @since 2.16
*/
protected DefaultDeserializationContext(DefaultDeserializationContext src,
DeserializerCache cache) {
super(src, cache);
}

/**
* Copy-constructor
*
* @since 2.4.4
*/
protected DefaultDeserializationContext(DefaultDeserializationContext src) {
Expand Down Expand Up @@ -296,6 +307,13 @@ public final KeyDeserializer keyDeserializerInstance(Annotated ann, Object deser
*/
public abstract DefaultDeserializationContext with(DeserializerFactory factory);

/**
* Fluent factory method used for constructing a new instance with cache instances provided by {@link CacheProvider}.
*
* @since 2.16
*/
public abstract DefaultDeserializationContext withCaches(CacheProvider cacheProvider);

/**
* Method called to create actual usable per-deserialization
* context instance.
Expand Down Expand Up @@ -384,7 +402,9 @@ public final static class Impl extends DefaultDeserializationContext
* {@link DeserializerCache}, given factory.
*/
public Impl(DeserializerFactory df) {
super(df, null);
// 04-Sep-2023, tatu: Not ideal (wrt not going via CacheProvider) but
// has to do for backwards compatibility:
super(df, new DeserializerCache());
}

private Impl(Impl src,
Expand All @@ -402,6 +422,13 @@ private Impl(Impl src, DeserializationConfig config) {
super(src, config);
}

/**
* @since 2.16
*/
private Impl(Impl src, DeserializerCache deserializerCache) {
super(src, deserializerCache);
}

@Override
public DefaultDeserializationContext copy() {
ClassUtil.verifyMustOverride(Impl.class, this, "copy");
Expand All @@ -424,5 +451,11 @@ public DefaultDeserializationContext createDummyInstance(DeserializationConfig c
public DefaultDeserializationContext with(DeserializerFactory factory) {
return new Impl(this, factory);
}

@Override // Since 2.16
public DefaultDeserializationContext withCaches(CacheProvider cacheProvider) {
return new Impl(this,
new DeserializerCache(cacheProvider.forDeserializerCache(_config)));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,13 @@ public DeserializerCache(LookupCache<JavaType, JsonDeserializer<Object>> cache)
_cachedDeserializers = cache;
}

/**
* @since 2.16
*/
public DeserializerCache emptyCopy() {
return new DeserializerCache(_cachedDeserializers.emptyCopy());
}

/*
/**********************************************************
/* JDK serialization handling
Expand Down
Loading

0 comments on commit 341f8d3

Please sign in to comment.