Skip to content

Commit 245fe1c

Browse files
authored
Add a way to configure TypeFactory Jackson uses (#4115)
1 parent d5e0b43 commit 245fe1c

File tree

5 files changed

+148
-8
lines changed

5 files changed

+148
-8
lines changed

src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2297,6 +2297,7 @@ public ObjectMapper setCacheProvider(CacheProvider cacheProvider) {
22972297
_serializationConfig = _serializationConfig.with(cacheProvider);
22982298
_deserializationContext = _deserializationContext.withCaches(cacheProvider);
22992299
_serializerProvider = _serializerProvider.withCaches(cacheProvider);
2300+
_typeFactory = _typeFactory.withCaches(cacheProvider);
23002301
return this;
23012302
}
23022303

src/main/java/com/fasterxml/jackson/databind/cfg/CacheProvider.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,19 @@ public interface CacheProvider
2222
* @return {@link LookupCache} instance for constructing {@link DeserializerCache}.
2323
*/
2424
LookupCache<JavaType, JsonDeserializer<Object>> forDeserializerCache(DeserializationConfig config);
25-
25+
2626
/**
2727
* Method to provide a {@link LookupCache} instance for constructing {@link com.fasterxml.jackson.databind.ser.SerializerCache}.
2828
*
2929
* @return {@link LookupCache} instance for constructing {@link com.fasterxml.jackson.databind.ser.SerializerCache}.
3030
*/
3131
LookupCache<TypeKey, JsonSerializer<Object>> forSerializerCache(SerializationConfig config);
32+
33+
/**
34+
* Method to provide a {@link LookupCache} instance for constructing {@link com.fasterxml.jackson.databind.type.TypeFactory}.
35+
*
36+
* @return {@link LookupCache} instance for constructing {@link com.fasterxml.jackson.databind.type.TypeFactory}.
37+
*/
38+
LookupCache<Object, JavaType> forTypeFactory();
39+
3240
}

src/main/java/com/fasterxml/jackson/databind/cfg/DefaultCacheProvider.java

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.fasterxml.jackson.databind.*;
44
import com.fasterxml.jackson.databind.deser.DeserializerCache;
55
import com.fasterxml.jackson.databind.ser.SerializerCache;
6+
import com.fasterxml.jackson.databind.type.TypeFactory;
67
import com.fasterxml.jackson.databind.util.LRUMap;
78
import com.fasterxml.jackson.databind.util.LookupCache;
89
import com.fasterxml.jackson.databind.util.TypeKey;
@@ -21,7 +22,7 @@ public class DefaultCacheProvider
2122
private static final long serialVersionUID = 1L;
2223

2324
private final static DefaultCacheProvider DEFAULT
24-
= new DefaultCacheProvider(DeserializerCache.DEFAULT_MAX_CACHE_SIZE, SerializerCache.DEFAULT_MAX_CACHE_SIZE);
25+
= new DefaultCacheProvider(DeserializerCache.DEFAULT_MAX_CACHE_SIZE, SerializerCache.DEFAULT_MAX_CACHE_SIZE, TypeFactory.DEFAULT_MAX_CACHE_SIZE);
2526

2627
/**
2728
* Maximum size of the {@link LookupCache} instance constructed by {@link #forDeserializerCache(DeserializationConfig)}.
@@ -36,17 +37,25 @@ public class DefaultCacheProvider
3637
* @see Builder#maxSerializerCacheSize(int)
3738
*/
3839
protected final int _maxSerializerCacheSize;
39-
40+
41+
/**
42+
* Maximum size of the {@link LookupCache} instance constructed by {@link #forTypeFactory()}.
43+
*
44+
* @see Builder#maxTypeFactoryCacheSize(int)
45+
*/
46+
protected final int _maxTypeFactoryCacheSize;
47+
4048
/*
4149
/**********************************************************************
4250
/* Life cycle
4351
/**********************************************************************
4452
*/
4553

46-
protected DefaultCacheProvider(int maxDeserializerCacheSize, int maxSerializerCacheSize)
54+
protected DefaultCacheProvider(int maxDeserializerCacheSize, int maxSerializerCacheSize, int maxTypeFactoryCacheSize)
4755
{
4856
_maxDeserializerCacheSize = maxDeserializerCacheSize;
4957
_maxSerializerCacheSize = maxSerializerCacheSize;
58+
_maxTypeFactoryCacheSize = maxTypeFactoryCacheSize;
5059
}
5160

5261
/*
@@ -84,6 +93,11 @@ public LookupCache<TypeKey, JsonSerializer<Object>> forSerializerCache(Serializa
8493
return _buildCache(_maxSerializerCacheSize);
8594
}
8695

96+
@Override
97+
public LookupCache<Object, JavaType> forTypeFactory() {
98+
return _buildCache(_maxTypeFactoryCacheSize);
99+
}
100+
87101
/*
88102
/**********************************************************
89103
/* Overridable factory methods
@@ -127,11 +141,19 @@ public static class Builder {
127141
*/
128142
private int _maxSerializerCacheSize;
129143

144+
/**
145+
* Maximum Size of the {@link LookupCache} instance created by {@link #forTypeFactory()}.
146+
* Corresponds to {@link DefaultCacheProvider#_maxTypeFactoryCacheSize}.
147+
*/
148+
private int _maxTypeFactoryCacheSize;
149+
130150
Builder() { }
131151

132152
/**
133153
* Define the maximum size of the {@link LookupCache} instance constructed by {@link #forDeserializerCache(DeserializationConfig)}
134154
* and {@link #_buildCache(int)}.
155+
* <p>
156+
* Note that specifying a maximum size of zero prevents values from being retained in the cache.
135157
*
136158
* @param maxDeserializerCacheSize Size for the {@link LookupCache} to use within {@link DeserializerCache}
137159
* @return this builder
@@ -149,6 +171,8 @@ public Builder maxDeserializerCacheSize(int maxDeserializerCacheSize) {
149171
/**
150172
* Define the maximum size of the {@link LookupCache} instance constructed by {@link #forSerializerCache(SerializationConfig)}
151173
* and {@link #_buildCache(int)}
174+
* <p>
175+
* Note that specifying a maximum size of zero prevents values from being retained in the cache.
152176
*
153177
* @param maxSerializerCacheSize Size for the {@link LookupCache} to use within {@link SerializerCache}
154178
* @return this builder
@@ -163,13 +187,31 @@ public Builder maxSerializerCacheSize(int maxSerializerCacheSize) {
163187
return this;
164188
}
165189

190+
/**
191+
* Define the maximum size of the {@link LookupCache} instance constructed by {@link #forTypeFactory()}
192+
* and {@link #_buildCache(int)}
193+
* <p>
194+
* Note that specifying a maximum size of zero prevents values from being retained in the cache.
195+
*
196+
* @param maxTypeFactoryCacheSize Size for the {@link LookupCache} to use within {@link com.fasterxml.jackson.databind.type.TypeFactory}
197+
* @return this builder
198+
* @throws IllegalArgumentException if {@code maxTypeFactoryCacheSize} is negative
199+
*/
200+
public Builder maxTypeFactoryCacheSize(int maxTypeFactoryCacheSize) {
201+
if (maxTypeFactoryCacheSize < 0) {
202+
throw new IllegalArgumentException("Cannot set maxTypeFactoryCacheSize to a negative value");
203+
}
204+
_maxTypeFactoryCacheSize = maxTypeFactoryCacheSize;
205+
return this;
206+
}
207+
166208
/**
167209
* Constructs a {@link DefaultCacheProvider} with the provided configuration values, using defaults where not specified.
168210
*
169211
* @return A {@link DefaultCacheProvider} instance with the specified configuration
170212
*/
171213
public DefaultCacheProvider build() {
172-
return new DefaultCacheProvider(_maxDeserializerCacheSize, _maxSerializerCacheSize);
214+
return new DefaultCacheProvider(_maxDeserializerCacheSize, _maxSerializerCacheSize, _maxTypeFactoryCacheSize);
173215
}
174216
}
175217
}

src/main/java/com/fasterxml/jackson/databind/type/TypeFactory.java

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import com.fasterxml.jackson.core.type.TypeReference;
1313
import com.fasterxml.jackson.databind.JavaType;
1414
import com.fasterxml.jackson.databind.JsonNode;
15+
import com.fasterxml.jackson.databind.cfg.CacheProvider;
1516
import com.fasterxml.jackson.databind.util.ArrayBuilders;
1617
import com.fasterxml.jackson.databind.util.ClassUtil;
1718
import com.fasterxml.jackson.databind.util.LRUMap;
@@ -70,6 +71,15 @@ public class TypeFactory // note: was final in 2.9, removed from 2.10
7071
{
7172
private static final long serialVersionUID = 1L;
7273

74+
/**
75+
* Default size used to construct {@link #_typeCache}.
76+
*
77+
* Used to be passed inline.
78+
*
79+
* @since 2.16
80+
*/
81+
public static final int DEFAULT_MAX_CACHE_SIZE = 200;
82+
7383
private final static JavaType[] NO_TYPES = new JavaType[0];
7484

7585
/**
@@ -178,7 +188,7 @@ public class TypeFactory // note: was final in 2.9, removed from 2.10
178188
*/
179189

180190
private TypeFactory() {
181-
this(new LRUMap<>(16, 200));
191+
this(new LRUMap<>(16, DEFAULT_MAX_CACHE_SIZE));
182192
}
183193

184194
/**
@@ -198,7 +208,7 @@ protected TypeFactory(LookupCache<Object,JavaType> typeCache, TypeParser p,
198208
TypeModifier[] mods, ClassLoader classLoader)
199209
{
200210
if (typeCache == null) {
201-
typeCache = new LRUMap<>(16, 200);
211+
typeCache = new LRUMap<>(16, DEFAULT_MAX_CACHE_SIZE);
202212
}
203213
_typeCache = typeCache;
204214
// As per [databind#894] must ensure we have back-linkage from TypeFactory:
@@ -260,11 +270,23 @@ public TypeFactory withCache(LRUMap<Object,JavaType> cache) {
260270
* bigger maximum size.
261271
*
262272
* @since 2.12
273+
* @deprecated Since 2.16. Use {@link #withCaches(CacheProvider)} instead.
263274
*/
264275
public TypeFactory withCache(LookupCache<Object,JavaType> cache) {
265276
return new TypeFactory(cache, _parser, _modifiers, _classLoader);
266277
}
267278

279+
/**
280+
* Mutant factory method that will construct a new {@link TypeFactory}
281+
* with cache instances provided by {@link CacheProvider}.
282+
*
283+
* @since 2.16
284+
*/
285+
public TypeFactory withCaches(CacheProvider cacheProvider) {
286+
return new TypeFactory(cacheProvider.forTypeFactory(),
287+
_parser, _modifiers, _classLoader);
288+
}
289+
268290
/**
269291
* Method used to access the globally shared instance, which has
270292
* no custom configuration. Used by {@code ObjectMapper} to

src/test/java/com/fasterxml/jackson/databind/cfg/CacheProviderTest.java

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import com.fasterxml.jackson.databind.util.LRUMap;
66
import com.fasterxml.jackson.databind.util.LookupCache;
77
import com.fasterxml.jackson.databind.util.TypeKey;
8+
import java.util.List;
89
import org.junit.Test;
910

1011
import java.util.HashMap;
@@ -90,6 +91,11 @@ public LookupCache<JavaType, JsonDeserializer<Object>> forDeserializerCache(Dese
9091
return _cache;
9192
}
9293

94+
@Override
95+
public LookupCache<Object, JavaType> forTypeFactory() {
96+
return new LRUMap<>(16, 64);
97+
}
98+
9399
@Override
94100
public LookupCache<TypeKey, JsonSerializer<Object>> forSerializerCache(SerializationConfig config) {
95101
return new LRUMap<>(8, 64);
@@ -110,6 +116,10 @@ public LookupCache<JavaType, JsonDeserializer<Object>> forDeserializerCache(Dese
110116
}
111117

112118
@Override
119+
public LookupCache<Object, JavaType> forTypeFactory() {
120+
return new LRUMap<>(16, 64);
121+
}
122+
113123
public LookupCache<TypeKey, JsonSerializer<Object>> forSerializerCache(SerializationConfig config) {
114124
return _cache;
115125
}
@@ -128,6 +138,40 @@ public JsonSerializer<Object> put(TypeKey key, JsonSerializer<Object> value) {
128138
return super.put(key, value);
129139
}
130140
}
141+
142+
static class CustomTypeFactoryCacheProvider implements CacheProvider {
143+
144+
final CustomTestTypeFactoryCache _cache = new CustomTestTypeFactoryCache();
145+
146+
@Override
147+
public LookupCache<JavaType, JsonDeserializer<Object>> forDeserializerCache(DeserializationConfig config) {
148+
return new LRUMap<>(16, 64);
149+
}
150+
151+
@Override
152+
public LookupCache<Object, JavaType> forTypeFactory() {
153+
return _cache;
154+
}
155+
156+
public LookupCache<TypeKey, JsonSerializer<Object>> forSerializerCache(SerializationConfig config) {
157+
return new LRUMap<>(16, 64);
158+
}
159+
}
160+
161+
static class CustomTestTypeFactoryCache extends LRUMap<Object,JavaType> {
162+
163+
public boolean _isInvoked = false;
164+
165+
public CustomTestTypeFactoryCache() {
166+
super(8, 16);
167+
}
168+
169+
@Override
170+
public JavaType putIfAbsent(Object key, JavaType value) {
171+
_isInvoked = true;
172+
return super.putIfAbsent(key, value);
173+
}
174+
}
131175

132176
/*
133177
/**********************************************************************
@@ -186,7 +230,7 @@ public void testDefaultCacheProviderSharesCache() throws Exception
186230
.cacheProvider(cacheProvider)
187231
.build();
188232

189-
// Act
233+
// Act
190234
// 3. Add two different types to each mapper cache
191235
mapper1.readValue("{\"point\":24}", RandomBean.class);
192236
mapper2.readValue("{\"height\":24}", AnotherBean.class);
@@ -205,10 +249,12 @@ public void testBuilderValueValidation() throws Exception
205249
DefaultCacheProvider.builder()
206250
.maxDeserializerCacheSize(0)
207251
.maxSerializerCacheSize(0)
252+
.maxTypeFactoryCacheSize(0)
208253
.build();
209254
DefaultCacheProvider.builder()
210255
.maxDeserializerCacheSize(Integer.MAX_VALUE)
211256
.maxSerializerCacheSize(Integer.MAX_VALUE)
257+
.maxTypeFactoryCacheSize(Integer.MAX_VALUE)
212258
.build();
213259

214260
// fail cases
@@ -224,6 +270,12 @@ public void testBuilderValueValidation() throws Exception
224270
} catch (IllegalArgumentException e) {
225271
assertTrue(e.getMessage().contains("Cannot set maxSerializerCacheSize to a negative value"));
226272
}
273+
try {
274+
DefaultCacheProvider.builder().maxTypeFactoryCacheSize(-1);
275+
fail("Should not reach here");
276+
} catch (IllegalArgumentException e) {
277+
assertTrue(e.getMessage().contains("Cannot set maxTypeFactoryCacheSize to a negative value"));
278+
}
227279
}
228280

229281
/**
@@ -258,4 +310,19 @@ private void _verifySerializeSuccess(CacheProvider cacheProvider) throws Excepti
258310
assertEquals("{\"slide\":123}",
259311
mapper.writeValueAsString(new SerBean()));
260312
}
313+
314+
/**
315+
* Sanity test for serialization with {@link CacheProvider#forTypeFactory()}
316+
*/
317+
@Test
318+
public void sanityCheckTypeFactoryCacheSize() throws Exception
319+
{
320+
// custom
321+
CustomTypeFactoryCacheProvider customProvider = new CustomTypeFactoryCacheProvider();
322+
ObjectMapper mapper = JsonMapper.builder()
323+
.cacheProvider(customProvider)
324+
.build();
325+
mapper.getTypeFactory().constructParametricType(List.class, HashMap.class);
326+
assertTrue(customProvider._cache._isInvoked);
327+
}
261328
}

0 commit comments

Comments
 (0)