From fdd9241ffe63dd30ab229d36402cac5c136479c1 Mon Sep 17 00:00:00 2001 From: Gregorius Soedharmo Date: Thu, 29 Apr 2021 21:35:54 +0700 Subject: [PATCH] Change the base class of PrimitiveSerializers to SerializerWithStringManifest (#4989) * Change the base class of PrimitiveSerializers to SerializerWithStringManifest * Add backward compatibility to the wire format * Update API Approver list (cherry picked from commit 25246ac0356d31fe0b6dcfb5594be9769dd53a69) --- .../CoreAPISpec.ApproveRemote.approved.txt | 6 +- .../Serialization/PrimitiveSerializersSpec.cs | 18 ++++- .../Serialization/PrimitiveSerializers.cs | 74 +++++++++++++++---- 3 files changed, 78 insertions(+), 20 deletions(-) diff --git a/src/core/Akka.API.Tests/CoreAPISpec.ApproveRemote.approved.txt b/src/core/Akka.API.Tests/CoreAPISpec.ApproveRemote.approved.txt index 5d7e06bafe6..d22b3977c6b 100644 --- a/src/core/Akka.API.Tests/CoreAPISpec.ApproveRemote.approved.txt +++ b/src/core/Akka.API.Tests/CoreAPISpec.ApproveRemote.approved.txt @@ -429,11 +429,11 @@ namespace Akka.Remote.Serialization public override string Manifest(object obj) { } public override byte[] ToBinary(object obj) { } } - public sealed class PrimitiveSerializers : Akka.Serialization.Serializer + public sealed class PrimitiveSerializers : Akka.Serialization.SerializerWithStringManifest { public PrimitiveSerializers(Akka.Actor.ExtendedActorSystem system) { } - public override bool IncludeManifest { get; } - public override object FromBinary(byte[] bytes, System.Type type) { } + public override object FromBinary(byte[] bytes, string manifest) { } + public override string Manifest(object obj) { } public override byte[] ToBinary(object obj) { } } public class ProtobufSerializer : Akka.Serialization.Serializer diff --git a/src/core/Akka.Remote.Tests/Serialization/PrimitiveSerializersSpec.cs b/src/core/Akka.Remote.Tests/Serialization/PrimitiveSerializersSpec.cs index 818bbdd0e0a..1dc23b73e6e 100644 --- a/src/core/Akka.Remote.Tests/Serialization/PrimitiveSerializersSpec.cs +++ b/src/core/Akka.Remote.Tests/Serialization/PrimitiveSerializersSpec.cs @@ -8,6 +8,7 @@ using Akka.Configuration; using Akka.Remote.Configuration; using Akka.Remote.Serialization; +using Akka.Serialization; using Akka.TestKit; using FluentAssertions; using Xunit; @@ -57,16 +58,29 @@ public void Can_serialize_String(string value) private T AssertAndReturn(T message) { - var serializer = Sys.Serialization.FindSerializerFor(message); + var serializer = (SerializerWithStringManifest)Sys.Serialization.FindSerializerFor(message); serializer.Should().BeOfType(); var serializedBytes = serializer.ToBinary(message); - return (T)serializer.FromBinary(serializedBytes, typeof(T)); + var manifest = serializer.Manifest(message); + return (T)serializer.FromBinary(serializedBytes, manifest); + } + + private T AssertCrossPlatformAndReturn(T message) + { + var serializer = (SerializerWithStringManifest)Sys.Serialization.FindSerializerFor(message); + serializer.Should().BeOfType(); + var serializedBytes = serializer.ToBinary(message); + // GetType() will make sure that each namespace is compatible with the serializer + // as the test is run on each platform. + return (T)serializer.FromBinary(serializedBytes, message.GetType()); } private void AssertEqual(T message) { var deserialized = AssertAndReturn(message); Assert.Equal(message, deserialized); + deserialized = AssertCrossPlatformAndReturn(message); + Assert.Equal(message, deserialized); } } } diff --git a/src/core/Akka.Remote/Serialization/PrimitiveSerializers.cs b/src/core/Akka.Remote/Serialization/PrimitiveSerializers.cs index a768ff04ceb..f31ae584c20 100644 --- a/src/core/Akka.Remote/Serialization/PrimitiveSerializers.cs +++ b/src/core/Akka.Remote/Serialization/PrimitiveSerializers.cs @@ -13,8 +13,22 @@ namespace Akka.Remote.Serialization { - public sealed class PrimitiveSerializers : Serializer + public sealed class PrimitiveSerializers : SerializerWithStringManifest { + internal const string StringManifest = "S"; + internal const string Int32Manifest = "I"; + internal const string Int64Manifest = "L"; + + // .Net Core manifests + internal const string StringManifestNetCore = "System.String, System.Private.CoreLib"; + internal const string Int32ManifestNetCore = "System.Int32, System.Private.CoreLib"; + internal const string Int64ManifestNetCore = "System.Int64, System.Private.CoreLib"; + + // .Net Framework manifests + internal const string StringManifestNetFx = "System.String, mscorlib"; + internal const string Int32ManifestNetFx = "System.Int32, mscorlib"; + internal const string Int64ManifestNetFx = "System.Int64, mscorlib"; + /// /// Initializes a new instance of the class. /// @@ -23,28 +37,58 @@ public PrimitiveSerializers(ExtendedActorSystem system) : base(system) { } - /// - public override bool IncludeManifest { get; } = true; - /// public override byte[] ToBinary(object obj) { - var str = obj as string; - if (str != null) return Encoding.UTF8.GetBytes(str); - if (obj is int) return BitConverter.GetBytes((int)obj); - if (obj is long) return BitConverter.GetBytes((long)obj); - - throw new ArgumentException($"Cannot serialize object of type [{obj.GetType().TypeQualifiedName()}]"); + switch (obj) + { + case string s: + return Encoding.UTF8.GetBytes(s); + case int i: + return BitConverter.GetBytes(i); + case long l: + return BitConverter.GetBytes(l); + default: + throw new ArgumentException($"Cannot serialize object of type [{obj.GetType()}]"); + } } /// - public override object FromBinary(byte[] bytes, Type type) + public override object FromBinary(byte[] bytes, string manifest) { - if (type == typeof(string)) return Encoding.UTF8.GetString(bytes); - if (type == typeof(int)) return BitConverter.ToInt32(bytes, 0); - if (type == typeof(long)) return BitConverter.ToInt64(bytes, 0); + switch (manifest) + { + case StringManifest: + case StringManifestNetCore: + case StringManifestNetFx: + return Encoding.UTF8.GetString(bytes); + case Int32Manifest: + case Int32ManifestNetCore: + case Int32ManifestNetFx: + return BitConverter.ToInt32(bytes, 0); + case Int64Manifest: + case Int64ManifestNetCore: + case Int64ManifestNetFx: + return BitConverter.ToInt64(bytes, 0); + default: + throw new ArgumentException($"Unimplemented deserialization of message with manifest [{manifest}] in [${GetType()}]"); + } + } - throw new ArgumentException($"Unimplemented deserialization of message with manifest [{type.TypeQualifiedName()}] in [${nameof(PrimitiveSerializers)}]"); + /// + public override string Manifest(object obj) + { + switch (obj) + { + case string _: + return StringManifest; + case int _: + return Int32Manifest; + case long _: + return Int64Manifest; + default: + throw new ArgumentException($"Cannot serialize object of type [{obj.GetType()}] in [{GetType()}]"); + } } } }