Merge pull request #28920 from akka/wip-28918-cbor-patriknw

JacksonCborSerializer should use CBOR ofc, #28918
This commit is contained in:
Patrik Nordwall 2020-04-20 16:06:04 +02:00 committed by GitHub
commit 1f448cae2c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 91 additions and 9 deletions

View file

@ -86,3 +86,32 @@ has been prepared to accept those shorter forms but still emits the old long man
This means that a rolling update will have to go through at least one of 2.6.2, 2.6.3 or 2.6.4 when upgrading to This means that a rolling update will have to go through at least one of 2.6.2, 2.6.3 or 2.6.4 when upgrading to
2.6.5 or higher or else cluster nodes will not be able to communicate during the rolling update. 2.6.5 or higher or else cluster nodes will not be able to communicate during the rolling update.
### 2.6.5 JacksonCborSerializer
Issue: [#28918](https://github.com/akka/akka/issues/28918). JacksonCborSerializer was using plain JSON format
instead of CBOR.
If you have `jackson-cbor` in your `serialization-bindings` a rolling upgrade will have to go through 2.6.5 when
upgrading to 2.6.5 or higher.
In Akka 2.6.5 the `jackson-cbor` binding will still serialize to JSON format to support rolling update from 2.6.4.
It also adds a new binding to be able to deserialize CBOR format when rolling update from 2.6.5 to 2.6.6.
In Akka 2.6.6 the `jackson-cbor` binding will serialize to CBOR and that can be deserialized by 2.6.5. Old
data, such as persistent events, can still be deserialized.
You can start using CBOR format already with Akka 2.6.5 without waiting for the 2.6.6 release. First, perform
a rolling update to Akka 2.6.5 using default configuration. Then change the configuration to:
```
akka.actor {
serializers {
jackson-cbor = "akka.serialization.jackson.JacksonCborSerializer"
}
serialization-identifiers {
jackson-cbor = 33
}
}
```
Perform a second rolling update with the new configuration.

View file

@ -175,6 +175,13 @@ akka.serialization.jackson {
# override the settings in 'akka.serialization.jackson' # override the settings in 'akka.serialization.jackson'
jackson-cbor {} jackson-cbor {}
# Issue #28918 for compatibility with data serialized with JacksonCborSerializer in
# Akka 2.6.4 or earlier, which was plain JSON format.
jackson-cbor-264 = ${akka.serialization.jackson.jackson-cbor}
# Issue #28918 temporary in Akka 2.6.5 to support rolling update to 2.6.6.
jackson-cbor-265 = ${akka.serialization.jackson.jackson-cbor}
} }
#//#features #//#features
@ -195,11 +202,23 @@ akka.serialization.jackson.jackson-json.compression {
akka.actor { akka.actor {
serializers { serializers {
jackson-json = "akka.serialization.jackson.JacksonJsonSerializer" jackson-json = "akka.serialization.jackson.JacksonJsonSerializer"
jackson-cbor = "akka.serialization.jackson.JacksonCborSerializer" jackson-cbor = "akka.serialization.jackson.JacksonJsonSerializer"
# Issue #28918 for compatibility with data serialized with JacksonCborSerializer in
# Akka 2.6.4 or earlier, which was plain JSON format.
jackson-cbor-264 = "akka.serialization.jackson.JacksonJsonSerializer"
# Issue #28918 temporary in Akka 2.6.5 to support rolling update to 2.6.6.
jackson-cbor-265 = "akka.serialization.jackson.JacksonCborSerializer"
} }
serialization-identifiers { serialization-identifiers {
jackson-json = 31 jackson-json = 31
jackson-cbor = 32 jackson-cbor = 32
# Issue #28918 for compatibility with data serialized with JacksonCborSerializer in
# Akka 2.6.4 or earlier, which was plain JSON format.
jackson-cbor-264 = 32
# Issue #28918 temporary in Akka 2.6.5 to support rolling update to 2.6.6.
jackson-cbor-265 = 33
} }
serialization-bindings { serialization-bindings {
# Define bindings for classes or interfaces use Jackson serializer, e.g. # Define bindings for classes or interfaces use Jackson serializer, e.g.

View file

@ -11,6 +11,7 @@ import scala.collection.immutable
import scala.compat.java8.OptionConverters._ import scala.compat.java8.OptionConverters._
import scala.util.Failure import scala.util.Failure
import scala.util.Success import scala.util.Success
import akka.actor.ActorSystem import akka.actor.ActorSystem
import akka.actor.ClassicActorSystemProvider import akka.actor.ClassicActorSystemProvider
import akka.actor.DynamicAccess import akka.actor.DynamicAccess
@ -70,9 +71,15 @@ object JacksonObjectMapperProvider extends ExtensionId[JacksonObjectMapperProvid
config: Config, config: Config,
baseJsonFactory: Option[JsonFactory]): JsonFactory = { baseJsonFactory: Option[JsonFactory]): JsonFactory = {
val jsonFactoryBuilder = baseJsonFactory match { val jsonFactory: JsonFactory = baseJsonFactory match {
case Some(jsonFactory) => new JsonFactoryBuilder(jsonFactory) case Some(factory) =>
case None => new JsonFactoryBuilder() // Issue #28918 not possible to use new JsonFactoryBuilder(jsonFactory) here.
// It doesn't preserve the formatParserFeatures and formatGeneratorFeatures in
// CBORFactor. Therefore we use JsonFactory and configure the features with mappedFeature
// instead of using JsonFactoryBuilder (new in Jackson 2.10.0).
factory
case None =>
new JsonFactoryBuilder().build()
} }
val configuredStreamReadFeatures = val configuredStreamReadFeatures =
@ -82,7 +89,7 @@ object JacksonObjectMapperProvider extends ExtensionId[JacksonObjectMapperProvid
val streamReadFeatures = val streamReadFeatures =
objectMapperFactory.overrideConfiguredStreamReadFeatures(bindingName, configuredStreamReadFeatures) objectMapperFactory.overrideConfiguredStreamReadFeatures(bindingName, configuredStreamReadFeatures)
streamReadFeatures.foreach { streamReadFeatures.foreach {
case (feature, value) => jsonFactoryBuilder.configure(feature, value) case (feature, value) => jsonFactory.configure(feature.mappedFeature, value)
} }
val configuredStreamWriteFeatures = val configuredStreamWriteFeatures =
@ -92,7 +99,7 @@ object JacksonObjectMapperProvider extends ExtensionId[JacksonObjectMapperProvid
val streamWriteFeatures = val streamWriteFeatures =
objectMapperFactory.overrideConfiguredStreamWriteFeatures(bindingName, configuredStreamWriteFeatures) objectMapperFactory.overrideConfiguredStreamWriteFeatures(bindingName, configuredStreamWriteFeatures)
streamWriteFeatures.foreach { streamWriteFeatures.foreach {
case (feature, value) => jsonFactoryBuilder.configure(feature, value) case (feature, value) => jsonFactory.configure(feature.mappedFeature, value)
} }
val configuredJsonReadFeatures = val configuredJsonReadFeatures =
@ -102,7 +109,7 @@ object JacksonObjectMapperProvider extends ExtensionId[JacksonObjectMapperProvid
val jsonReadFeatures = val jsonReadFeatures =
objectMapperFactory.overrideConfiguredJsonReadFeatures(bindingName, configuredJsonReadFeatures) objectMapperFactory.overrideConfiguredJsonReadFeatures(bindingName, configuredJsonReadFeatures)
jsonReadFeatures.foreach { jsonReadFeatures.foreach {
case (feature, value) => jsonFactoryBuilder.configure(feature, value) case (feature, value) => jsonFactory.configure(feature.mappedFeature, value)
} }
val configuredJsonWriteFeatures = val configuredJsonWriteFeatures =
@ -112,10 +119,10 @@ object JacksonObjectMapperProvider extends ExtensionId[JacksonObjectMapperProvid
val jsonWriteFeatures = val jsonWriteFeatures =
objectMapperFactory.overrideConfiguredJsonWriteFeatures(bindingName, configuredJsonWriteFeatures) objectMapperFactory.overrideConfiguredJsonWriteFeatures(bindingName, configuredJsonWriteFeatures)
jsonWriteFeatures.foreach { jsonWriteFeatures.foreach {
case (feature, value) => jsonFactoryBuilder.configure(feature, value) case (feature, value) => jsonFactory.configure(feature.mappedFeature, value)
} }
jsonFactoryBuilder.build() jsonFactory
} }
private def configureObjectMapperFeatures( private def configureObjectMapperFeatures(

View file

@ -4,6 +4,7 @@
package akka.serialization.jackson package akka.serialization.jackson
import java.nio.charset.StandardCharsets
import java.time.Duration import java.time.Duration
import java.time.Instant import java.time.Instant
import java.time.LocalDateTime import java.time.LocalDateTime
@ -17,6 +18,7 @@ import java.util.logging.FileHandler
import scala.collection.immutable import scala.collection.immutable
import scala.concurrent.duration.FiniteDuration import scala.concurrent.duration.FiniteDuration
import scala.concurrent.duration._ import scala.concurrent.duration._
import akka.actor.ActorRef import akka.actor.ActorRef
import akka.actor.ActorSystem import akka.actor.ActorSystem
import akka.actor.Address import akka.actor.Address
@ -569,6 +571,17 @@ class JacksonJsonSerializerSpec extends JacksonSerializerSpec("jackson-json") {
""")(sys => checkSerialization(Elephant("Dumbo", 1), sys)) """)(sys => checkSerialization(Elephant("Dumbo", 1), sys))
} }
} }
// issue #28918
"cbor compatibility for reading json" in {
val msg = SimpleCommand("abc")
val jsonSerializer = serializerFor(msg)
jsonSerializer.identifier should ===(31)
val manifest = jsonSerializer.manifest(msg)
val bytes = jsonSerializer.toBinary(msg)
val deserialized = serialization().deserialize(bytes, 32, manifest).get
deserialized should be(msg)
}
} }
} }
@ -626,6 +639,20 @@ abstract class JacksonSerializerSpec(serializerName: String)
val manifest = serializer.manifest(obj) val manifest = serializer.manifest(obj)
val serializerId = serializer.identifier val serializerId = serializer.identifier
val blob = serializeToBinary(obj) val blob = serializeToBinary(obj)
// Issue #28918, check that CBOR format is used (not JSON).
if (blob.length > 0) {
serializer match {
case _: JacksonJsonSerializer =>
if (!JacksonSerializer.isGZipped(blob))
new String(blob.take(1), StandardCharsets.UTF_8) should ===("{")
case _: JacksonCborSerializer =>
new String(blob.take(1), StandardCharsets.UTF_8) should !==("{")
case _ =>
throw new IllegalArgumentException(s"Unexpected serializer $serializer")
}
}
val deserialized = deserializeFromBinary(blob, serializerId, manifest, sys) val deserialized = deserializeFromBinary(blob, serializerId, manifest, sys)
deserialized should ===(obj) deserialized should ===(obj)
} }