jackson 2.16 support (#564)

* jackson 2.15 support

* Update JacksonObjectMapperProvider.scala

Update JacksonObjectMapperProvider.scala

Update JacksonObjectMapperProvider.scala

Update JacksonObjectMapperProvider.scala

* jackson 2.15.3

* jackson 2.16

* Update reference.conf

* add max-document-length

* Update .scala-steward.conf

* Update reference.conf

* 2.16.1

* Create JacksonFactorySpec.scala

* Update JacksonFactorySpec.scala

* add blank line
This commit is contained in:
PJ Fanning 2024-01-06 20:07:03 +01:00 committed by GitHub
parent f361cb2f59
commit 3f97d9fe04
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 151 additions and 32 deletions

View file

@ -1,3 +1,7 @@
updates.pin = [
{ groupId = "com.fasterxml.jackson.core", version = "2.16." }
]
updates.ignore = [ updates.ignore = [
{ groupId = "org.scalameta", artifactId = "scalafmt-core" } { groupId = "org.scalameta", artifactId = "scalafmt-core" }
{ groupId = "org.scalameta", artifactId = "sbt-scalafmt" } { groupId = "org.scalameta", artifactId = "sbt-scalafmt" }

View file

@ -33,7 +33,7 @@ object Dependencies {
val protobufJavaVersion = "3.20.3" val protobufJavaVersion = "3.20.3"
val logbackVersion = "1.3.14" val logbackVersion = "1.3.14"
val jacksonCoreVersion = "2.14.3" val jacksonCoreVersion = "2.16.1"
val jacksonDatabindVersion = jacksonCoreVersion val jacksonDatabindVersion = jacksonCoreVersion
val scala212Version = "2.12.18" val scala212Version = "2.12.18"

View file

@ -37,6 +37,27 @@ pekko.serialization.jackson {
} }
#//#stream-read-constraints
pekko.serialization.jackson {
read {
# see https://www.javadoc.io/static/com.fasterxml.jackson.core/jackson-core/2.16.1/com/fasterxml/jackson/core/StreamReadConstraints.html
# these defaults are the same as the defaults in `StreamReadConstraints`
max-nesting-depth = 1000
max-number-length = 1000
max-string-length = 20000000
max-name-length = 50000
# max-document-length of -1 means unlimited
max-document-length = -1
}
write {
# see https://www.javadoc.io/static/com.fasterxml.jackson.core/jackson-core/2.16.1/com/fasterxml/jackson/core/StreamWriteConstraints.html
# these defaults are the same as the defaults in `StreamWriteConstraints`
max-nesting-depth = 1000
}
}
#//#stream-read-constraints
#//#features #//#features
pekko.serialization.jackson { pekko.serialization.jackson {
# Configuration of the ObjectMapper serialization features. # Configuration of the ObjectMapper serialization features.

View file

@ -17,40 +17,42 @@ import java.util.Optional
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
import scala.annotation.nowarn import scala.annotation.nowarn
import scala.collection.immutable import scala.collection.immutable
import scala.util.Failure import scala.util.{ Failure, Success }
import scala.util.Success import com.fasterxml.jackson.annotation.{ JsonAutoDetect, JsonCreator, PropertyAccessor }
import com.fasterxml.jackson.core.{
import com.fasterxml.jackson.annotation.JsonAutoDetect JsonFactory,
import com.fasterxml.jackson.annotation.JsonCreator JsonFactoryBuilder,
import com.fasterxml.jackson.annotation.PropertyAccessor JsonGenerator,
import com.fasterxml.jackson.core.JsonFactory JsonParser,
import com.fasterxml.jackson.core.JsonFactoryBuilder StreamReadConstraints,
import com.fasterxml.jackson.core.JsonGenerator StreamReadFeature,
import com.fasterxml.jackson.core.JsonParser StreamWriteConstraints,
import com.fasterxml.jackson.core.StreamReadFeature StreamWriteFeature
import com.fasterxml.jackson.core.StreamWriteFeature }
import com.fasterxml.jackson.core.json.JsonReadFeature import com.fasterxml.jackson.core.json.{ JsonReadFeature, JsonWriteFeature }
import com.fasterxml.jackson.core.json.JsonWriteFeature import com.fasterxml.jackson.databind.{
import com.fasterxml.jackson.databind.DeserializationFeature DeserializationFeature,
import com.fasterxml.jackson.databind.MapperFeature MapperFeature,
import com.fasterxml.jackson.databind.Module Module,
import com.fasterxml.jackson.databind.ObjectMapper ObjectMapper,
import com.fasterxml.jackson.databind.SerializationFeature SerializationFeature
}
import com.fasterxml.jackson.databind.json.JsonMapper import com.fasterxml.jackson.databind.json.JsonMapper
import com.fasterxml.jackson.module.paramnames.ParameterNamesModule import com.fasterxml.jackson.module.paramnames.ParameterNamesModule
import com.typesafe.config.Config import com.typesafe.config.Config
import org.apache.pekko import org.apache.pekko
import pekko.actor.ActorSystem import pekko.actor.{
import pekko.actor.ClassicActorSystemProvider ActorSystem,
import pekko.actor.DynamicAccess ClassicActorSystemProvider,
import pekko.actor.ExtendedActorSystem DynamicAccess,
import pekko.actor.Extension ExtendedActorSystem,
import pekko.actor.ExtensionId Extension,
import pekko.actor.ExtensionIdProvider ExtensionId,
ExtensionIdProvider
}
import pekko.actor.setup.Setup import pekko.actor.setup.Setup
import pekko.annotation.InternalStableApi import pekko.annotation.InternalStableApi
import pekko.event.Logging import pekko.event.{ Logging, LoggingAdapter }
import pekko.event.LoggingAdapter
import pekko.util.unused import pekko.util.unused
import pekko.util.OptionConverters._ import pekko.util.OptionConverters._
@ -68,7 +70,7 @@ object JacksonObjectMapperProvider extends ExtensionId[JacksonObjectMapperProvid
*/ */
def configForBinding(bindingName: String, systemConfig: Config): Config = { def configForBinding(bindingName: String, systemConfig: Config): Config = {
val basePath = "pekko.serialization.jackson" val basePath = "pekko.serialization.jackson"
val baseConf = systemConfig.getConfig("pekko.serialization.jackson") val baseConf = systemConfig.getConfig(basePath)
if (systemConfig.hasPath(s"$basePath.$bindingName")) if (systemConfig.hasPath(s"$basePath.$bindingName"))
systemConfig.getConfig(s"$basePath.$bindingName").withFallback(baseConf) systemConfig.getConfig(s"$basePath.$bindingName").withFallback(baseConf)
else else
@ -81,15 +83,31 @@ object JacksonObjectMapperProvider extends ExtensionId[JacksonObjectMapperProvid
config: Config, config: Config,
baseJsonFactory: Option[JsonFactory]): JsonFactory = { baseJsonFactory: Option[JsonFactory]): JsonFactory = {
val streamReadConstraints = StreamReadConstraints.builder()
.maxNestingDepth(config.getInt("read.max-nesting-depth"))
.maxNumberLength(config.getInt("read.max-number-length"))
.maxStringLength(config.getInt("read.max-string-length"))
.maxNameLength(config.getInt("read.max-name-length"))
.maxDocumentLength(config.getLong("read.max-document-length"))
.build()
val streamWriteConstraints = StreamWriteConstraints.builder()
.maxNestingDepth(config.getInt("write.max-nesting-depth"))
.build()
val jsonFactory: JsonFactory = baseJsonFactory match { val jsonFactory: JsonFactory = baseJsonFactory match {
case Some(factory) => case Some(factory) =>
// Issue #28918 not possible to use new JsonFactoryBuilder(jsonFactory) here. // Issue #28918 not possible to use new JsonFactoryBuilder(jsonFactory) here.
// It doesn't preserve the formatParserFeatures and formatGeneratorFeatures in // It doesn't preserve the formatParserFeatures and formatGeneratorFeatures in
// CBORFactor. Therefore we use JsonFactory and configure the features with mappedFeature // CBORFactor. Therefore we use JsonFactory and configure the features with mappedFeature
// instead of using JsonFactoryBuilder (new in Jackson 2.10.0). // instead of using JsonFactoryBuilder (new in Jackson 2.10.0).
factory factory.setStreamReadConstraints(streamReadConstraints)
factory.setStreamWriteConstraints(streamWriteConstraints)
case None => case None =>
new JsonFactoryBuilder().build() new JsonFactoryBuilder()
.streamReadConstraints(streamReadConstraints)
.streamWriteConstraints(streamWriteConstraints)
.build()
} }
val configuredStreamReadFeatures = val configuredStreamReadFeatures =

View file

@ -0,0 +1,76 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.pekko.serialization.jackson
import com.typesafe.config.ConfigFactory
import org.scalatest.BeforeAndAfterAll
import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpecLike
import org.apache.pekko
import pekko.actor.{ ActorSystem, ExtendedActorSystem }
import pekko.testkit.TestKit
class JacksonFactorySpec extends TestKit(ActorSystem("JacksonFactorySpec"))
with AnyWordSpecLike with Matchers with BeforeAndAfterAll {
private val defaultConfig = ConfigFactory.defaultReference()
private val dynamicAccess = system.asInstanceOf[ExtendedActorSystem].dynamicAccess
private val objectMapperFactory = new JacksonObjectMapperFactory
override def afterAll(): Unit = {
super.afterAll()
system.terminate()
}
"Jackson Factory config" must {
"support StreamReadConstraints" in {
val bindingName = "testJackson"
val maxNumLen = 987
val maxStringLen = 1234567
val maxDocLen = 123456789L
val maxNestingDepth = 5
val config = ConfigFactory.parseString(
s"""pekko.serialization.jackson.read.max-number-length=$maxNumLen
|pekko.serialization.jackson.read.max-string-length=$maxStringLen
|pekko.serialization.jackson.read.max-document-length=$maxDocLen
|pekko.serialization.jackson.read.max-nesting-depth=$maxNestingDepth
|""".stripMargin)
.withFallback(defaultConfig)
val jacksonConfig = JacksonObjectMapperProvider.configForBinding(bindingName, config)
val mapper = JacksonObjectMapperProvider.createObjectMapper(
bindingName, None, objectMapperFactory, jacksonConfig, dynamicAccess, None)
val streamReadConstraints = mapper.getFactory.streamReadConstraints()
streamReadConstraints.getMaxNumberLength shouldEqual maxNumLen
streamReadConstraints.getMaxStringLength shouldEqual maxStringLen
streamReadConstraints.getMaxDocumentLength shouldEqual maxDocLen
streamReadConstraints.getMaxNestingDepth shouldEqual maxNestingDepth
}
"support StreamWriteConstraints" in {
val bindingName = "testJackson"
val maxNestingDepth = 54321
val config = ConfigFactory.parseString(
s"pekko.serialization.jackson.write.max-nesting-depth=$maxNestingDepth")
.withFallback(defaultConfig)
val jacksonConfig = JacksonObjectMapperProvider.configForBinding(bindingName, config)
val mapper = JacksonObjectMapperProvider.createObjectMapper(
bindingName, None, objectMapperFactory, jacksonConfig, dynamicAccess, None)
val streamWriteConstraints = mapper.getFactory.streamWriteConstraints()
streamWriteConstraints.getMaxNestingDepth shouldEqual maxNestingDepth
}
}
}