Skip to content

Commit c63fb87

Browse files
feat(client): detect binary incompatible jackson versions (#315)
1 parent e1fc80f commit c63fb87

2 files changed

Lines changed: 43 additions & 5 deletions

File tree

orb-java-core/src/main/kotlin/com/withorb/api/core/ObjectMappers.kt

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,15 @@ import com.fasterxml.jackson.databind.SerializationFeature
99
import com.fasterxml.jackson.databind.SerializerProvider
1010
import com.fasterxml.jackson.databind.cfg.CoercionAction.Fail
1111
import com.fasterxml.jackson.databind.cfg.CoercionInputShape.Integer
12+
import com.fasterxml.jackson.databind.exc.InvalidDefinitionException
13+
import com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException
1214
import com.fasterxml.jackson.databind.json.JsonMapper
1315
import com.fasterxml.jackson.databind.module.SimpleModule
1416
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module
1517
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule
1618
import com.fasterxml.jackson.module.kotlin.jacksonMapperBuilder
19+
import com.withorb.api.errors.OrbException
20+
import com.withorb.api.errors.OrbInvalidDataException
1721
import java.io.InputStream
1822

1923
fun jsonMapper(): JsonMapper =
@@ -43,3 +47,38 @@ private object InputStreamJsonSerializer : BaseSerializer<InputStream>(InputStre
4347
}
4448
}
4549
}
50+
51+
@JvmSynthetic
52+
internal fun enhanceJacksonException(fallbackMessage: String, e: Exception): Exception {
53+
// These exceptions should only happen if our code is wrong OR if the user is using a binary
54+
// incompatible version of `com.fasterxml.jackson.core:jackson-databind`:
55+
// https://javadoc.io/static/com.fasterxml.jackson.core/jackson-databind/2.18.1/index.html
56+
val isUnexpectedException =
57+
e is UnrecognizedPropertyException || e is InvalidDefinitionException
58+
if (!isUnexpectedException) {
59+
return OrbInvalidDataException(fallbackMessage, e)
60+
}
61+
62+
val jacksonVersion = JsonMapper::class.java.`package`.implementationVersion
63+
if (jacksonVersion.isNullOrEmpty() || jacksonVersion == COMPILED_JACKSON_VERSION) {
64+
return OrbInvalidDataException(fallbackMessage, e)
65+
}
66+
67+
return OrbException(
68+
"""
69+
Jackson threw an unexpected exception and its runtime version ($jacksonVersion) mismatches the version the SDK was compiled with ($COMPILED_JACKSON_VERSION).
70+
71+
You may be using a version of `com.fasterxml.jackson.core:jackson-databind` that's not binary compatible with the SDK.
72+
73+
This can happen if you are either:
74+
1. Directly depending on a different Jackson version
75+
2. Depending on some library that depends on a different Jackson version, potentially transitively
76+
77+
Double-check that you are depending on a compatible Jackson version.
78+
"""
79+
.trimIndent(),
80+
e,
81+
)
82+
}
83+
84+
const val COMPILED_JACKSON_VERSION = "2.18.1"

orb-java-core/src/main/kotlin/com/withorb/api/core/handlers/JsonHandler.kt

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,17 @@ package com.withorb.api.core.handlers
44

55
import com.fasterxml.jackson.databind.json.JsonMapper
66
import com.fasterxml.jackson.module.kotlin.jacksonTypeRef
7+
import com.withorb.api.core.enhanceJacksonException
78
import com.withorb.api.core.http.HttpResponse
89
import com.withorb.api.core.http.HttpResponse.Handler
9-
import com.withorb.api.errors.OrbException
1010

1111
@JvmSynthetic
1212
internal inline fun <reified T> jsonHandler(jsonMapper: JsonMapper): Handler<T> =
1313
object : Handler<T> {
14-
override fun handle(response: HttpResponse): T {
14+
override fun handle(response: HttpResponse): T =
1515
try {
16-
return jsonMapper.readValue(response.body(), jacksonTypeRef())
16+
jsonMapper.readValue(response.body(), jacksonTypeRef())
1717
} catch (e: Exception) {
18-
throw OrbException("Error reading response", e)
18+
throw enhanceJacksonException("Error reading response", e)
1919
}
20-
}
2120
}

0 commit comments

Comments
 (0)