Skip to content

Commit

Permalink
Merge branch 'dev' into actor-ref-provider-aliases
Browse files Browse the repository at this point in the history
  • Loading branch information
Horusiath authored Feb 21, 2017
2 parents 754d14f + c8181f0 commit 56fb285
Show file tree
Hide file tree
Showing 13 changed files with 658 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="HyperionConfigTests.cs" />
<Compile Include="HyperionTests.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
Expand Down Expand Up @@ -83,6 +84,9 @@
<Name>Akka.Serialization.TestKit</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
//-----------------------------------------------------------------------
// <copyright file="HyperionConfigTests.cs" company="Akka.NET Project">
// Copyright (C) 2009-2016 Lightbend Inc. <http://www.lightbend.com>
// Copyright (C) 2013-2016 Akka.NET project <https://github.com/akkadotnet/akka.net>
// </copyright>
//-----------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Linq;
using Akka.Actor;
using Akka.Configuration;
using Xunit;

namespace Akka.Serialization.Hyperion.Tests
{
public class HyperionConfigTests
{
[Fact]
public void Hyperion_serializer_should_have_correct_defaults()
{
var config = ConfigurationFactory.ParseString(@"
akka.actor {
serializers.hyperion = ""Akka.Serialization.HyperionSerializer, Akka.Serialization.Hyperion""
serialization-bindings {
""System.Object"" = hyperion
}
}
");
using (var system = ActorSystem.Create(nameof(HyperionConfigTests), config))
{
var serializer = (HyperionSerializer)system.Serialization.FindSerializerForType(typeof(object));
Assert.Equal(true, serializer.Settings.VersionTolerance);
Assert.Equal(true, serializer.Settings.PreserveObjectReferences);
Assert.Equal("NoKnownTypes", serializer.Settings.KnownTypesProvider.Name);
}
}

[Fact]
public void Hyperion_serializer_should_allow_to_setup_custom_flags()
{
var config = ConfigurationFactory.ParseString(@"
akka.actor {
serializers.hyperion = ""Akka.Serialization.HyperionSerializer, Akka.Serialization.Hyperion""
serialization-bindings {
""System.Object"" = hyperion
}
serialization-settings.hyperion {
preserve-object-references = false
version-tolerance = false
}
}
");
using (var system = ActorSystem.Create(nameof(HyperionConfigTests), config))
{
var serializer = (HyperionSerializer)system.Serialization.FindSerializerForType(typeof(object));
Assert.Equal(false, serializer.Settings.VersionTolerance);
Assert.Equal(false, serializer.Settings.PreserveObjectReferences);
Assert.Equal("NoKnownTypes", serializer.Settings.KnownTypesProvider.Name);
}
}

[Fact]
public void Hyperion_serializer_should_allow_to_setup_custom_types_provider_with_default_constructor()
{
var config = ConfigurationFactory.ParseString(@"
akka.actor {
serializers.hyperion = ""Akka.Serialization.HyperionSerializer, Akka.Serialization.Hyperion""
serialization-bindings {
""System.Object"" = hyperion
}
serialization-settings.hyperion {
known-types-provider = ""Akka.Serialization.Hyperion.Tests.DummyTypesProviderWithDefaultCtor, Akka.Serialization.Hyperion.Tests""
}
}
");
using (var system = ActorSystem.Create(nameof(HyperionConfigTests), config))
{
var serializer = (HyperionSerializer)system.Serialization.FindSerializerForType(typeof(object));
Assert.Equal(true, serializer.Settings.VersionTolerance);
Assert.Equal(true, serializer.Settings.PreserveObjectReferences);
Assert.Equal(typeof(DummyTypesProviderWithDefaultCtor), serializer.Settings.KnownTypesProvider);
}
}

[Fact]
public void Hyperion_serializer_should_allow_to_setup_custom_types_provider_with_non_default_constructor()
{
var config = ConfigurationFactory.ParseString(@"
akka.actor {
serializers.hyperion = ""Akka.Serialization.HyperionSerializer, Akka.Serialization.Hyperion""
serialization-bindings {
""System.Object"" = hyperion
}
serialization-settings.hyperion {
known-types-provider = ""Akka.Serialization.Hyperion.Tests.DummyTypesProvider, Akka.Serialization.Hyperion.Tests""
}
}
");
using (var system = ActorSystem.Create(nameof(HyperionConfigTests), config))
{
var serializer = (HyperionSerializer)system.Serialization.FindSerializerForType(typeof(object));
Assert.Equal(true, serializer.Settings.VersionTolerance);
Assert.Equal(true, serializer.Settings.PreserveObjectReferences);
Assert.Equal(typeof(DummyTypesProvider), serializer.Settings.KnownTypesProvider);
}
}
}

class DummyTypesProvider : IKnownTypesProvider
{
public DummyTypesProvider(ExtendedActorSystem system)
{
if (system == null)
throw new ArgumentNullException(nameof(system));
}

public IEnumerable<Type> GetKnownTypes() => Enumerable.Empty<Type>();
}

class DummyTypesProviderWithDefaultCtor : IKnownTypesProvider
{
public IEnumerable<Type> GetKnownTypes() => Enumerable.Empty<Type>();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,13 @@
</ItemGroup>
<ItemGroup>
<Compile Include="HyperionSerializer.cs" />
<Compile Include="IKnownTypesProvider.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="Akka.Serialization.Hyperion.nuspec" />
<None Include="packages.config" />
<None Include="reference.conf" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\core\Akka\Akka.csproj">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@

using System;
using System.IO;
using System.Linq;
using Akka.Actor;
using Akka.Configuration;
using Akka.Util;
using Hyperion;

Expand All @@ -19,45 +21,66 @@ namespace Akka.Serialization
/// </summary>
public class HyperionSerializer : Serializer
{
/// <summary>
/// Settings used for an underlying Hyperion serializer implementation.
/// </summary>
public readonly HyperionSerializerSettings Settings;

private readonly Hyperion.Serializer _serializer;

/// <summary>
/// Initializes a new instance of the <see cref="HyperionSerializer"/> class.
/// </summary>
/// <param name="system">The actor system to associate with this serializer.</param>
public HyperionSerializer(ExtendedActorSystem system) : base(system)
public HyperionSerializer(ExtendedActorSystem system)
: this(system, HyperionSerializerSettings.Default)
{
}

/// <summary>
/// Initializes a new instance of the <see cref="HyperionSerializer"/> class.
/// </summary>
/// <param name="system">The actor system to associate with this serializer.</param>
/// <param name="config">Configuration passed from related HOCON config path.</param>
public HyperionSerializer(ExtendedActorSystem system, Config config)
: this(system, HyperionSerializerSettings.Create(config))
{
}

/// <summary>
/// Initializes a new instance of the <see cref="HyperionSerializer"/> class.
/// </summary>
/// <param name="system">The actor system to associate with this serializer.</param>
/// <param name="settings">Serializer settings.</param>
public HyperionSerializer(ExtendedActorSystem system, HyperionSerializerSettings settings)
: base(system)
{
this.Settings = settings;
var akkaSurrogate =
Surrogate
.Create<ISurrogated, ISurrogate>(
from => from.ToSurrogate(system),
to => to.FromSurrogate(system));

var provider = CreateKnownTypesProvider(system, settings.KnownTypesProvider);

_serializer =
new Hyperion.Serializer(new SerializerOptions(
preserveObjectReferences: true,
versionTolerance: true,
surrogates: new[]
{
akkaSurrogate
}));
preserveObjectReferences: settings.PreserveObjectReferences,
versionTolerance: settings.VersionTolerance,
surrogates: new[] { akkaSurrogate },
knownTypes: provider.GetKnownTypes()));
}

/// <summary>
/// Completely unique value to identify this implementation of Serializer, used to optimize network traffic
/// </summary>
public override int Identifier
{
get { return -5; }
}
public override int Identifier => -5;

/// <summary>
/// Returns whether this serializer needs a manifest in the fromBinary method
/// </summary>
public override bool IncludeManifest
{
get { return false; }
}
public override bool IncludeManifest => false;

/// <summary>
/// Serializes the given object into a byte array
Expand Down Expand Up @@ -87,5 +110,98 @@ public override object FromBinary(byte[] bytes, Type type)
return res;
}
}

private IKnownTypesProvider CreateKnownTypesProvider(ExtendedActorSystem system, Type type)
{
var ctors = type.GetConstructors();
var ctor = ctors.FirstOrDefault(c =>
{
var parameters = c.GetParameters();
return parameters.Length == 1 && (parameters[0].ParameterType == typeof(ActorSystem)
|| parameters[0].ParameterType == typeof(ExtendedActorSystem));
});

return ctor == null
? (IKnownTypesProvider) Activator.CreateInstance(type)
: (IKnownTypesProvider) ctor.Invoke(new object[] {system});
}
}

/// <summary>
/// A typed settings class for a <see cref="HyperionSerializer"/>.
/// </summary>
public sealed class HyperionSerializerSettings
{
/// <summary>
/// Default settings used by <see cref="HyperionSerializer"/> when no config has been specified.
/// </summary>
public static readonly HyperionSerializerSettings Default = new HyperionSerializerSettings(
preserveObjectReferences: true,
versionTolerance: true,
knownTypesProvider: typeof(NoKnownTypes));

/// <summary>
/// Creates a new instance of <see cref="HyperionSerializerSettings"/> using provided HOCON config.
/// Config can contain several key-values, that are mapped to a class fields:
/// 1. `preserve-object-references` (boolean) mapped to <see cref="PreserveObjectReferences"/>
/// 2. `version-tolerance` (boolean) mapped to <see cref="VersionTolerance"/>
/// 3. `known-types-provider` (fully qualified type name) mapped to <see cref="KnownTypesProvider"/>
/// </summary>
/// <exception cref="ArgumentNullException">Raised when <paramref name="config"/> was not provided.</exception>
/// <exception cref="ArgumentException">Raised when `known-types-provider` type doesn't implement <see cref="IKnownTypesProvider"/> interface.</exception>
/// <param name="config"></param>
/// <returns></returns>
public static HyperionSerializerSettings Create(Config config)
{
if (config == null) throw new ArgumentNullException(nameof(config), "HyperionSerializerSettings require a config, default path: `akka.serializers.hyperion`");

var typeName = config.GetString("known-types-provider");
var type = !string.IsNullOrEmpty(typeName) ? Type.GetType(typeName, true) : null;

return new HyperionSerializerSettings(
preserveObjectReferences: config.GetBoolean("preserve-object-references", true),
versionTolerance: config.GetBoolean("version-tolerance", true),
knownTypesProvider: type);
}

/// <summary>
/// When true, it tells <see cref="HyperionSerializer"/> to keep
/// track of references in serialized/deserialized object graph.
/// </summary>
public readonly bool PreserveObjectReferences;

/// <summary>
/// When true, it tells <see cref="HyperionSerializer"/> to encode
/// a list of currently serialized fields into type manifest.
/// </summary>
public readonly bool VersionTolerance;

/// <summary>
/// A type implementing <see cref="IKnownTypesProvider"/>, that will
/// be used when <see cref="HyperionSerializer"/> is being constructed
/// to provide a list of message types that are supposed to be known
/// implicitly by all communicating parties. Implementing class must
/// provide either a default constructor or a constructor taking
/// <see cref="ExtendedActorSystem"/> as its only parameter.
/// </summary>
public readonly Type KnownTypesProvider;

/// <summary>
/// Creates a new instance of a <see cref="HyperionSerializerSettings"/>.
/// </summary>
/// <param name="preserveObjectReferences">Flag which determines if serializer should keep track of references in serialized object graph.</param>
/// <param name="versionTolerance">Flag which determines if field data should be serialized as part of type manifest.</param>
/// <param name="knownTypesProvider">Type implementing <see cref="IKnownTypesProvider"/> to be used to determine a list of types implicitly known by all cooperating serializer.</param>
/// <exception cref="ArgumentException">Raised when `known-types-provider` type doesn't implement <see cref="IKnownTypesProvider"/> interface.</exception>
public HyperionSerializerSettings(bool preserveObjectReferences, bool versionTolerance, Type knownTypesProvider)
{
knownTypesProvider = knownTypesProvider ?? typeof(NoKnownTypes);
if (!typeof(IKnownTypesProvider).IsAssignableFrom(knownTypesProvider))
throw new ArgumentException($"Known types provider must implement an interface {typeof(IKnownTypesProvider).FullName}");

PreserveObjectReferences = preserveObjectReferences;
VersionTolerance = versionTolerance;
KnownTypesProvider = knownTypesProvider;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//-----------------------------------------------------------------------
// <copyright file="HyperionSerializer.cs" company="Akka.NET Project">
// Copyright (C) 2009-2016 Lightbend Inc. <http://www.lightbend.com>
// Copyright (C) 2013-2016 Akka.NET project <https://github.com/akkadotnet/akka.net>
// </copyright>
//-----------------------------------------------------------------------

using System;
using System.Collections.Generic;

namespace Akka.Serialization
{
/// <summary>
/// Interface that can be implemented in order to determine some
/// custom logic, that's going to provide a list of types that
/// are known to be shared for all corresponding parties during
/// remote communication.
/// </summary>
public interface IKnownTypesProvider
{
IEnumerable<Type> GetKnownTypes();
}

internal sealed class NoKnownTypes : IKnownTypesProvider
{
public IEnumerable<Type> GetKnownTypes() => new Type[0];
}
}
Loading

0 comments on commit 56fb285

Please sign in to comment.