Skip to content

Commit 9b6fdca

Browse files
committed
remove use of ClassKey (#622)
1 parent 964026d commit 9b6fdca

File tree

6 files changed

+192
-36
lines changed

6 files changed

+192
-36
lines changed

build.sbt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,15 @@ mimaBinaryIssueFilters ++= Seq(
167167
ProblemFilters.exclude[ReversedMissingMethodProblem]("com.fasterxml.jackson.module.scala.introspect.ScalaAnnotationIntrospectorModule.com$fasterxml$jackson$module$scala$introspect$ScalaAnnotationIntrospectorModule$_setter_$overrideMap_="),
168168
ProblemFilters.exclude[ReversedMissingMethodProblem]("com.fasterxml.jackson.module.scala.introspect.ScalaAnnotationIntrospectorModule.com$fasterxml$jackson$module$scala$introspect$ScalaAnnotationIntrospectorModule$$_shouldSupportScala3Classes_="),
169169
ProblemFilters.exclude[ReversedMissingMethodProblem]("com.fasterxml.jackson.module.scala.introspect.ScalaAnnotationIntrospectorModule.com$fasterxml$jackson$module$scala$introspect$ScalaAnnotationIntrospectorModule$$_shouldSupportScala3Classes"),
170+
ProblemFilters.exclude[ReversedMissingMethodProblem]("com.fasterxml.jackson.module.scala.introspect.ScalaAnnotationIntrospectorModule.com$fasterxml$jackson$module$scala$introspect$ScalaAnnotationIntrospectorModule$$_lookupCacheFactory"),
171+
ProblemFilters.exclude[ReversedMissingMethodProblem]("com.fasterxml.jackson.module.scala.introspect.ScalaAnnotationIntrospectorModule.com$fasterxml$jackson$module$scala$introspect$ScalaAnnotationIntrospectorModule$$_lookupCacheFactory_="),
172+
ProblemFilters.exclude[ReversedMissingMethodProblem]("com.fasterxml.jackson.module.scala.introspect.ScalaAnnotationIntrospectorModule.com$fasterxml$jackson$module$scala$introspect$ScalaAnnotationIntrospectorModule$$_descriptorCacheSize"),
173+
ProblemFilters.exclude[ReversedMissingMethodProblem]("com.fasterxml.jackson.module.scala.introspect.ScalaAnnotationIntrospectorModule.com$fasterxml$jackson$module$scala$introspect$ScalaAnnotationIntrospectorModule$$_descriptorCacheSize_="),
174+
ProblemFilters.exclude[ReversedMissingMethodProblem]("com.fasterxml.jackson.module.scala.introspect.ScalaAnnotationIntrospectorModule.com$fasterxml$jackson$module$scala$introspect$ScalaAnnotationIntrospectorModule$$_scalaTypeCacheSize"),
175+
ProblemFilters.exclude[ReversedMissingMethodProblem]("com.fasterxml.jackson.module.scala.introspect.ScalaAnnotationIntrospectorModule.com$fasterxml$jackson$module$scala$introspect$ScalaAnnotationIntrospectorModule$$_scalaTypeCacheSize_="),
176+
ProblemFilters.exclude[ReversedMissingMethodProblem]("com.fasterxml.jackson.module.scala.introspect.ScalaAnnotationIntrospectorModule.setLookupCacheFactory"),
177+
ProblemFilters.exclude[ReversedMissingMethodProblem]("com.fasterxml.jackson.module.scala.introspect.ScalaAnnotationIntrospectorModule.setDescriptorCacheSize"),
178+
ProblemFilters.exclude[ReversedMissingMethodProblem]("com.fasterxml.jackson.module.scala.introspect.ScalaAnnotationIntrospectorModule.setScalaTypeCacheSize"),
170179
ProblemFilters.exclude[ReversedMissingMethodProblem]("com.fasterxml.jackson.module.scala.introspect.ScalaAnnotationIntrospectorModule.clearRegisteredReferencedTypes"),
171180
ProblemFilters.exclude[ReversedMissingMethodProblem]("com.fasterxml.jackson.module.scala.introspect.ScalaAnnotationIntrospectorModule.registerReferencedValueType"),
172181
ProblemFilters.exclude[ReversedMissingMethodProblem]("com.fasterxml.jackson.module.scala.introspect.ScalaAnnotationIntrospectorModule.getRegisteredReferencedValueType"),

src/main/scala-2.+/com/fasterxml/jackson/module/scala/ScalaObjectMapper.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -343,9 +343,9 @@ trait ScalaObjectMapper {
343343
* JSON. Same converters (serializers, deserializers) will be used as for
344344
* data binding, meaning same object mapper configuration works.
345345
*
346-
* @throws IllegalArgumentException If conversion fails due to incompatible type;
347-
* if so, root cause will contain underlying checked exception data binding
348-
* functionality threw
346+
* @throws java.lang.IllegalArgumentException If conversion fails due to incompatible type;
347+
* if so, root cause will contain underlying checked exception data
348+
* binding functionality threw
349349
*/
350350
def convertValue[T: Manifest](fromValue: Any): T = {
351351
convertValue(fromValue, constructType[T])

src/main/scala/com/fasterxml/jackson/module/scala/ClassTagExtensions.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -255,9 +255,9 @@ trait ClassTagExtensions {
255255
* JSON. Same converters (serializers, deserializers) will be used as for
256256
* data binding, meaning same object mapper configuration works.
257257
*
258-
* @throws IllegalArgumentException If conversion fails due to incompatible type;
259-
* if so, root cause will contain underlying checked exception data binding
260-
* functionality threw
258+
* @throws java.lang.IllegalArgumentException If conversion fails due to incompatible type;
259+
* if so, root cause will contain underlying checked exception data
260+
* binding functionality threw
261261
*/
262262
def convertValue[T: JavaTypeable](fromValue: Any): T = {
263263
convertValue(fromValue, constructType[T])
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package com.fasterxml.jackson.module.scala
2+
3+
import com.fasterxml.jackson.databind.util.{LRUMap, LookupCache}
4+
5+
/**
6+
* Factory for creating [[com.fasterxml.jackson.databind.util.LookupCache]] instances
7+
*/
8+
trait LookupCacheFactory {
9+
def createLookupCache[K, V](initialEntries: Int, maxEntries: Int): LookupCache[K, V]
10+
}
11+
12+
object DefaultLookupCacheFactory extends LookupCacheFactory {
13+
override def createLookupCache[K, V](initialEntries: Int, maxEntries: Int): LookupCache[K, V] = {
14+
new LRUMap[K, V](initialEntries, maxEntries)
15+
}
16+
}

src/main/scala/com/fasterxml/jackson/module/scala/introspect/ScalaAnnotationIntrospectorModule.scala

Lines changed: 92 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@ package com.fasterxml.jackson.module.scala.introspect
22

33
import com.fasterxml.jackson.annotation.JsonCreator
44
import com.fasterxml.jackson.core.Version
5-
import com.fasterxml.jackson.databind.`type`.{ClassKey, CollectionLikeType, MapLikeType, ReferenceType, SimpleType}
5+
import com.fasterxml.jackson.databind.`type`.{CollectionLikeType, MapLikeType, ReferenceType, SimpleType}
66
import com.fasterxml.jackson.databind.cfg.MapperConfig
77
import com.fasterxml.jackson.databind.deser.std.StdValueInstantiator
88
import com.fasterxml.jackson.databind.deser._
99
import com.fasterxml.jackson.databind.introspect._
10-
import com.fasterxml.jackson.databind.util.{AccessPattern, LRUMap, LookupCache}
10+
import com.fasterxml.jackson.databind.util.{AccessPattern, LookupCache}
1111
import com.fasterxml.jackson.databind.{BeanDescription, DeserializationConfig, DeserializationContext, JavaType, MapperFeature}
12-
import com.fasterxml.jackson.module.scala.JacksonModule
12+
import com.fasterxml.jackson.module.scala.{DefaultLookupCacheFactory, JacksonModule, LookupCacheFactory}
1313
import com.fasterxml.jackson.module.scala.util.Implicits._
1414

1515
import java.lang.annotation.Annotation
@@ -178,7 +178,7 @@ object ScalaAnnotationIntrospector extends NopAnnotationIntrospector with ValueI
178178
}
179179

180180
private def _descriptorFor(clz: Class[_]): Option[BeanDescriptor] = {
181-
val key = new ClassKey(clz)
181+
val key = clz.getName
182182
val isScala = {
183183
Option(ScalaAnnotationIntrospectorModule._scalaTypeCache.get(key)) match {
184184
case Some(result) => result
@@ -247,15 +247,80 @@ trait ScalaAnnotationIntrospectorModule extends JacksonModule {
247247
this += { _.appendAnnotationIntrospector(ScalaAnnotationIntrospector) }
248248
this += { _.addValueInstantiators(ScalaAnnotationIntrospector) }
249249

250-
private[introspect] var _descriptorCache: LookupCache[ClassKey, BeanDescriptor] =
251-
new LRUMap[ClassKey, BeanDescriptor](16, 100)
250+
private var _lookupCacheFactory: LookupCacheFactory = DefaultLookupCacheFactory
251+
private var _shouldSupportScala3Classes: Boolean = true
252+
private var _descriptorCacheSize: Int = 100
253+
private var _scalaTypeCacheSize: Int = 1000
252254

253-
private[introspect] var _scalaTypeCache: LookupCache[ClassKey, Boolean] =
254-
new LRUMap[ClassKey, Boolean](16, 1000)
255+
private[introspect] var _descriptorCache: LookupCache[String, BeanDescriptor] =
256+
_lookupCacheFactory.createLookupCache(16, _descriptorCacheSize)
257+
258+
private[introspect] var _scalaTypeCache: LookupCache[String, Boolean] =
259+
_lookupCacheFactory.createLookupCache(16, _scalaTypeCacheSize)
255260

256261
private[introspect] val overrideMap = MutableMap[Class[_], ClassOverrides]()
257262

258-
private var _shouldSupportScala3Classes = true
263+
/**
264+
* Replaces the [[LookupCacheFactory]]. The default factory uses [[com.fasterxml.jackson.databind.util.LRUMap]].
265+
* <p>
266+
* Note that this clears the existing cache entries. It is best to set this up before you start using
267+
* the Jackson Scala Module for serializing/deserializing.
268+
* </p>
269+
*
270+
* @param lookupCacheFactory new factory
271+
* @see [[setDescriptorCacheSize]]
272+
* @see [[setScalaTypeCacheSize]]
273+
* @since 2.14.3
274+
*/
275+
def setLookupCacheFactory(lookupCacheFactory: LookupCacheFactory): Unit = {
276+
_lookupCacheFactory = lookupCacheFactory
277+
recreateDescriptorCache()
278+
recreateScalaTypeCache()
279+
}
280+
281+
/**
282+
* Resize the <code>descriptorCache</code>. The default size is 100.
283+
* <p>
284+
* Note that this clears the existing cache entries. It is best to set this up before you start using
285+
* the Jackson Scala Module for serializing/deserializing.
286+
* </p>
287+
*
288+
* @param size new size for the cache
289+
* @see [[setScalaTypeCacheSize]]
290+
* @see [[setLookupCacheFactory]]
291+
* @since 2.14.3
292+
*/
293+
def setDescriptorCacheSize(size: Int): Unit = {
294+
_descriptorCacheSize = size
295+
recreateDescriptorCache()
296+
}
297+
298+
/**
299+
* Resize the <code>scalaTypeCache</code>. The default size is 1000.
300+
* <p>
301+
* Note that this clears the existing cache entries. It is best to set this up before you start using
302+
* the Jackson Scala Module for serializing/deserializing.
303+
* </p>
304+
*
305+
* @param size new size for the cache
306+
* @see [[setDescriptorCacheSize]]
307+
* @see [[setLookupCacheFactory]]
308+
* @since 2.14.3
309+
*/
310+
def setScalaTypeCacheSize(size: Int): Unit = {
311+
_scalaTypeCacheSize = size
312+
recreateScalaTypeCache()
313+
}
314+
315+
private def recreateDescriptorCache(): Unit = {
316+
_descriptorCache.clear()
317+
_descriptorCache = _lookupCacheFactory.createLookupCache(16, _descriptorCacheSize)
318+
}
319+
320+
private def recreateScalaTypeCache(): Unit = {
321+
_scalaTypeCache.clear()
322+
_scalaTypeCache = _lookupCacheFactory.createLookupCache(16, _scalaTypeCacheSize)
323+
}
259324

260325
/**
261326
* jackson-module-scala does not always properly handle deserialization of Options or Collections wrapping
@@ -316,7 +381,7 @@ trait ScalaAnnotationIntrospectorModule extends JacksonModule {
316381
}
317382

318383
/**
319-
* clears all the state associated with reference types
384+
* Clears all the state associated with reference types
320385
*
321386
* @see [[registerReferencedValueType]]
322387
* @see [[clearRegisteredReferencedTypes(Class[_])]]
@@ -326,7 +391,17 @@ trait ScalaAnnotationIntrospectorModule extends JacksonModule {
326391
overrideMap.clear()
327392
}
328393

329-
def setDescriptorCache(cache: LookupCache[ClassKey, BeanDescriptor]): LookupCache[ClassKey, BeanDescriptor] = {
394+
/**
395+
* Replace the <code>descriptorCache</code.
396+
*
397+
* @param cache new cache instance
398+
* @return the existing cache instance
399+
* @see [[setDescriptorCacheSize]]
400+
* @see [[setLookupCacheFactory]]
401+
* @deprecated key type will change to String in v2.15.0 and this function will be removed in a later release
402+
*/
403+
@deprecated("key type will change to String in v2.15.0 and this function will be removed in a later release", "2.14.3")
404+
def setDescriptorCache(cache: LookupCache[String, BeanDescriptor]): LookupCache[String, BeanDescriptor] = {
330405
val existingCache = _descriptorCache
331406
_descriptorCache = cache
332407
existingCache
@@ -336,10 +411,14 @@ trait ScalaAnnotationIntrospectorModule extends JacksonModule {
336411
* Override the default <code>scalaTypeCache</code>.
337412
*
338413
* @param cache new cache instance
339-
* @return old cache instance
414+
* @return existing cache instance
340415
* @since 2.14.0
416+
* @see [[setScalaTypeCacheSize]]
417+
* @see [[setLookupCacheFactory]]
418+
* @deprecated key type will change to String in v2.15.0 and this function will be removed in a later release
341419
*/
342-
def setScalaTypeCache(cache: LookupCache[ClassKey, Boolean]): LookupCache[ClassKey, Boolean] = {
420+
@deprecated("key type will change to String in v2.15.0 and this function will be removed in a later release", "2.14.3")
421+
def setScalaTypeCache(cache: LookupCache[String, Boolean]): LookupCache[String, Boolean] = {
343422
val existingCache = _scalaTypeCache
344423
_scalaTypeCache = cache
345424
existingCache

src/test/scala/com/fasterxml/jackson/module/scala/introspect/ScalaAnnotationIntrospectorTest.scala

Lines changed: 69 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ package introspect
44

55
import com.fasterxml.jackson.annotation.JsonProperty
66
import com.fasterxml.jackson.core.JsonGenerator
7-
import com.fasterxml.jackson.databind.`type`.ClassKey
87
import com.fasterxml.jackson.databind.json.JsonMapper
98
import com.fasterxml.jackson.databind.module.SimpleModule
109
import com.fasterxml.jackson.databind.ser.ContextualSerializer
@@ -38,18 +37,17 @@ object ScalaAnnotationIntrospectorTest {
3837

3938
case class CaseClassWithDefault(a: String = "defaultParam", b: Option[String] = Some("optionDefault"), c: Option[String])
4039

41-
class ConcurrentLookupCache[T]() extends LookupCache[ClassKey, T] {
42-
final private val cache = TrieMap.empty[ClassKey, T]
40+
class ConcurrentLookupCache[K, V]() extends LookupCache[K, V] {
41+
final private val cache = TrieMap.empty[K, V]
4342

44-
override def put(key: ClassKey, value: T): T =
45-
cache.put(key, value).getOrElse(None.orNull).asInstanceOf[T]
43+
override def put(key: K, value: V): V =
44+
cache.put(key, value).getOrElse(None.orNull).asInstanceOf[V]
4645

47-
override def putIfAbsent(key: ClassKey, value: T): T =
48-
cache.putIfAbsent(key, value).getOrElse(None.orNull).asInstanceOf[T]
46+
override def putIfAbsent(key: K, value: V): V =
47+
cache.putIfAbsent(key, value).getOrElse(None.orNull).asInstanceOf[V]
4948

50-
override def get(key: Any): T = key match {
51-
case classKey: ClassKey => cache.get(classKey).getOrElse(None.orNull).asInstanceOf[T]
52-
case _ => None.orNull.asInstanceOf[T]
49+
override def get(key: Any): V = {
50+
cache.get(key.asInstanceOf[K]).getOrElse(None.orNull).asInstanceOf[V]
5351
}
5452

5553
override def clear(): Unit = {
@@ -58,6 +56,11 @@ object ScalaAnnotationIntrospectorTest {
5856

5957
override def size: Int = cache.size
6058
}
59+
60+
object ConcurrentLookupCacheFactory extends LookupCacheFactory {
61+
override def createLookupCache[K, V](initialEntries: Int, maxEntries: Int): LookupCache[K, V] =
62+
new ConcurrentLookupCache[K, V]
63+
}
6164
}
6265

6366
class ScalaAnnotationIntrospectorTest extends FixtureAnyFlatSpec with Matchers {
@@ -231,10 +234,10 @@ class ScalaAnnotationIntrospectorTest extends FixtureAnyFlatSpec with Matchers {
231234
}
232235
}
233236

234-
it should "allow descriptor cache to be replaced" in { _ =>
237+
it should "allow descriptor cache to be replaced (old style)" in { _ =>
235238
val defaultCache = ScalaAnnotationIntrospectorModule._descriptorCache
236239
try {
237-
val cache = new ConcurrentLookupCache[BeanDescriptor]()
240+
val cache = new ConcurrentLookupCache[String, BeanDescriptor]()
238241
ScalaAnnotationIntrospectorModule.setDescriptorCache(cache)
239242
val builder = JsonMapper.builder().addModule(DefaultScalaModule)
240243
val mapper = builder.build()
@@ -244,16 +247,38 @@ class ScalaAnnotationIntrospectorTest extends FixtureAnyFlatSpec with Matchers {
244247
withoutDefault.a shouldEqual "notDefault"
245248

246249
cache.size shouldBe >=(1)
247-
cache.get(new ClassKey(classOf[CaseClassWithDefault])) should not be (null)
250+
cache.get(classOf[CaseClassWithDefault].getName) should not be (null)
248251
} finally {
249252
ScalaAnnotationIntrospectorModule.setDescriptorCache(defaultCache)
250253
}
251254
}
252255

253-
it should "allow scala type cache to be replaced" in { _ =>
256+
it should "allow descriptor cache to be replaced (new style)" in { _ =>
257+
val defaultCache = ScalaAnnotationIntrospectorModule._descriptorCache
258+
try {
259+
ScalaAnnotationIntrospectorModule.setLookupCacheFactory(ConcurrentLookupCacheFactory)
260+
val builder = JsonMapper.builder().addModule(DefaultScalaModule)
261+
val mapper = builder.build()
262+
val jsonWithKey = """{"a": "notDefault"}"""
263+
264+
val withoutDefault = mapper.readValue(jsonWithKey, classOf[CaseClassWithDefault])
265+
withoutDefault.a shouldEqual "notDefault"
266+
267+
ScalaAnnotationIntrospectorModule._descriptorCache shouldBe a[ConcurrentLookupCache[String, BeanDescriptor]]
268+
val cache = ScalaAnnotationIntrospectorModule._descriptorCache
269+
.asInstanceOf[ConcurrentLookupCache[String, BeanDescriptor]]
270+
cache.size shouldBe >=(1)
271+
cache.get(classOf[CaseClassWithDefault].getName) should not be (null)
272+
defaultCache.size() shouldEqual 0
273+
} finally {
274+
ScalaAnnotationIntrospectorModule.setLookupCacheFactory(DefaultLookupCacheFactory)
275+
}
276+
}
277+
278+
it should "allow scala type cache to be replaced (old style)" in { _ =>
254279
val defaultCache = ScalaAnnotationIntrospectorModule._scalaTypeCache
255280
try {
256-
val cache = new ConcurrentLookupCache[Boolean]()
281+
val cache = new ConcurrentLookupCache[String, Boolean]()
257282
ScalaAnnotationIntrospectorModule.setScalaTypeCache(cache)
258283
val builder = JsonMapper.builder().addModule(DefaultScalaModule)
259284
val mapper = builder.build()
@@ -263,16 +288,43 @@ class ScalaAnnotationIntrospectorTest extends FixtureAnyFlatSpec with Matchers {
263288
withoutDefault.a shouldEqual "notDefault"
264289

265290
cache.size shouldBe >=(1)
266-
cache.get(new ClassKey(classOf[CaseClassWithDefault])) shouldBe true
291+
cache.get(classOf[CaseClassWithDefault].getName) shouldBe true
267292

268293
val javaValueHolder = mapper.readValue("\"2\"", classOf[ValueHolder])
269294
javaValueHolder should not be(null)
270-
cache.get(new ClassKey(classOf[ValueHolder])) shouldBe false
295+
cache.get(classOf[ValueHolder].getName) shouldBe false
271296
} finally {
272297
ScalaAnnotationIntrospectorModule.setScalaTypeCache(defaultCache)
273298
}
274299
}
275300

301+
it should "allow scala type cache to be replaced (new style)" in { _ =>
302+
val defaultCache = ScalaAnnotationIntrospectorModule._scalaTypeCache
303+
try {
304+
ScalaAnnotationIntrospectorModule.setLookupCacheFactory(ConcurrentLookupCacheFactory)
305+
val builder = JsonMapper.builder().addModule(DefaultScalaModule)
306+
val mapper = builder.build()
307+
val jsonWithKey = """{"a": "notDefault"}"""
308+
309+
val withoutDefault = mapper.readValue(jsonWithKey, classOf[CaseClassWithDefault])
310+
withoutDefault.a shouldEqual "notDefault"
311+
312+
ScalaAnnotationIntrospectorModule._scalaTypeCache shouldBe a[ConcurrentLookupCache[String, Boolean]]
313+
val cache = ScalaAnnotationIntrospectorModule._scalaTypeCache
314+
.asInstanceOf[ConcurrentLookupCache[String, Boolean]]
315+
cache.size shouldBe >=(1)
316+
cache.get(classOf[CaseClassWithDefault].getName) shouldBe true
317+
318+
val javaValueHolder = mapper.readValue("\"2\"", classOf[ValueHolder])
319+
javaValueHolder should not be (null)
320+
cache.get(classOf[ValueHolder].getName) shouldBe false
321+
322+
defaultCache.size() shouldEqual 0
323+
} finally {
324+
ScalaAnnotationIntrospectorModule.setLookupCacheFactory(DefaultLookupCacheFactory)
325+
}
326+
}
327+
276328
it should "allow scala3 check to be disabled" in { _ =>
277329
ScalaAnnotationIntrospectorModule.shouldSupportScala3Classes() shouldBe true
278330
try {

0 commit comments

Comments
 (0)