mirror of https://github.com/dapr/dotnet-sdk.git
commit
a4e65731b2
|
|
@ -30,35 +30,6 @@ stages:
|
|||
command: 'test'
|
||||
arguments: '--configuration $(Configuration)'
|
||||
projects: 'code.sln'
|
||||
- task: EsrpCodeSigning@1
|
||||
displayName: 'ESRP CodeSign'
|
||||
condition: and(eq(variables['Configuration'], 'release'), or(contains(variables['Build.SourceBranch'], 'refs/heads/master'), contains(variables['Build.SourceBranch'], 'refs/heads/release')))
|
||||
inputs:
|
||||
ConnectedServiceName: 'ActionsSigning'
|
||||
FolderPath: 'bin/$(Configuration)'
|
||||
Pattern: 'Microsoft.Actions*.dll'
|
||||
UseMinimatch: true
|
||||
signConfigType: 'inlineSignParams'
|
||||
inlineOperation: |
|
||||
[
|
||||
{
|
||||
"keyCode": "$(ESRP_CODESIGN_KEYCODE)",
|
||||
"operationSetCode": "StrongNameSign",
|
||||
"parameters": [ ],
|
||||
"toolName": "sign",
|
||||
"toolVersion": "1.0"
|
||||
},
|
||||
{
|
||||
"keyCode": "$(ESRP_CODESIGN_KEYCODE)",
|
||||
"operationSetCode": "StrongNameVerify",
|
||||
"parameters": [ ],
|
||||
"toolName": "sign",
|
||||
"toolVersion": "1.0"
|
||||
}
|
||||
]
|
||||
SessionTimeout: '20'
|
||||
MaxConcurrency: '50'
|
||||
MaxRetryAttempts: '5'
|
||||
- task: EsrpCodeSigning@1
|
||||
displayName: 'ESRP Authenticode CodeSign'
|
||||
condition: and(eq(variables['Configuration'], 'release'), or(contains(variables['Build.SourceBranch'], 'refs/heads/master'), contains(variables['Build.SourceBranch'], 'refs/heads/release')))
|
||||
|
|
@ -109,13 +80,21 @@ stages:
|
|||
MaxConcurrency: '50'
|
||||
MaxRetryAttempts: '5'
|
||||
- task: DotNetCoreCLI@2
|
||||
displayName: 'Package nuget - $(Configuration)'
|
||||
displayName: 'Package Microsoft.Actions.Actors nuget - $(Configuration)'
|
||||
inputs:
|
||||
command: 'pack'
|
||||
arguments: '--configuration $(Configuration)'
|
||||
packagesToPack: 'src/Microsoft.Actions.Actors/Microsoft.Actions.Actors.csproj'
|
||||
nobuild: true
|
||||
versioningScheme: 'off'
|
||||
- task: DotNetCoreCLI@2
|
||||
displayName: 'Package Microsoft.Actions.Actors.AspNetCore nuget - $(Configuration)'
|
||||
inputs:
|
||||
command: 'pack'
|
||||
arguments: '--configuration $(Configuration)'
|
||||
packagesToPack: 'src/Microsoft.Actions.Actors.AspNetCore/Microsoft.Actions.Actors.AspNetCore.csproj'
|
||||
nobuild: true
|
||||
versioningScheme: 'off'
|
||||
- task: EsrpCodeSigning@1
|
||||
displayName: 'ESRP CodeSign - Nuget Package'
|
||||
condition: and(eq(variables['Configuration'], 'release'), or(contains(variables['Build.SourceBranch'], 'refs/heads/master'), contains(variables['Build.SourceBranch'], 'refs/heads/release')))
|
||||
|
|
|
|||
62
code.sln
62
code.sln
|
|
@ -1,25 +1,37 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.28307.645
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Actions.Actors", "src\Microsoft.Actions.Actors\Microsoft.Actions.Actors.csproj", "{C2DB4B64-B7C3-4FED-8753-C040F677C69A}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{C2DB4B64-B7C3-4FED-8753-C040F677C69A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C2DB4B64-B7C3-4FED-8753-C040F677C69A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C2DB4B64-B7C3-4FED-8753-C040F677C69A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C2DB4B64-B7C3-4FED-8753-C040F677C69A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {65220BF2-EAE1-4CB2-AA58-EBE80768CB40}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.28307.645
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Actions.Actors", "src\Microsoft.Actions.Actors\Microsoft.Actions.Actors.csproj", "{C2DB4B64-B7C3-4FED-8753-C040F677C69A}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Actions.Actors.Test", "test\Microsoft.Actions.Actors.Test\Microsoft.Actions.Actors.Test.csproj", "{41BF4392-54BD-4FE7-A3EB-CD045F88CA9A}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Actions.Actors.AspNetCore", "src\Microsoft.Actions.Actors.AspNetCore\Microsoft.Actions.Actors.AspNetCore.csproj", "{B9C12532-0969-4DAC-A2F8-CA9208D7A901}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{C2DB4B64-B7C3-4FED-8753-C040F677C69A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C2DB4B64-B7C3-4FED-8753-C040F677C69A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C2DB4B64-B7C3-4FED-8753-C040F677C69A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C2DB4B64-B7C3-4FED-8753-C040F677C69A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{41BF4392-54BD-4FE7-A3EB-CD045F88CA9A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{41BF4392-54BD-4FE7-A3EB-CD045F88CA9A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{41BF4392-54BD-4FE7-A3EB-CD045F88CA9A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{41BF4392-54BD-4FE7-A3EB-CD045F88CA9A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{B9C12532-0969-4DAC-A2F8-CA9208D7A901}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{B9C12532-0969-4DAC-A2F8-CA9208D7A901}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B9C12532-0969-4DAC-A2F8-CA9208D7A901}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B9C12532-0969-4DAC-A2F8-CA9208D7A901}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {65220BF2-EAE1-4CB2-AA58-EBE80768CB40}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -13,7 +13,7 @@
|
|||
<MinorVersion>0</MinorVersion>
|
||||
<BuildVersion>0</BuildVersion>
|
||||
<Revision>0</Revision>
|
||||
<PreviewTag>-preview001</PreviewTag>
|
||||
<PreviewTag>-preview01</PreviewTag>
|
||||
|
||||
<Version>$(MajorVersion).$(MinorVersion).$(BuildVersion).$(Revision)</Version>
|
||||
<AssemblyVersion>$(Version)</AssemblyVersion>
|
||||
|
|
|
|||
|
|
@ -33,8 +33,7 @@
|
|||
<!-- Enable delay signing with Key.snk -->
|
||||
<PropertyGroup>
|
||||
<SignAssembly>true</SignAssembly>
|
||||
<DelaySign>true</DelaySign>
|
||||
<AssemblyOriginatorKeyFile>$(MSBuildThisFileDirectory)Key.snk</AssemblyOriginatorKeyFile>
|
||||
<AssemblyOriginatorKeyFile>$(MSBuildThisFileDirectory)cs_sdk.snk</AssemblyOriginatorKeyFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -0,0 +1,34 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors.AspNetCore
|
||||
{
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
|
||||
internal class ActionsActorSetupFilter : IStartupFilter
|
||||
{
|
||||
public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next)
|
||||
{
|
||||
// Adds routes for Actors interaction.
|
||||
return app =>
|
||||
{
|
||||
var actorRouteBuilder = new RouteBuilder(app);
|
||||
actorRouteBuilder.AddActionsConfigRoute();
|
||||
actorRouteBuilder.AddGetSupportedActorTypesRoute();
|
||||
actorRouteBuilder.AddActorActivationRoute();
|
||||
actorRouteBuilder.AddActorDeactivationRoute();
|
||||
actorRouteBuilder.AddActorMethodRoute();
|
||||
actorRouteBuilder.AddReminderRoute();
|
||||
actorRouteBuilder.AddTimerRoute();
|
||||
|
||||
app.UseRouter(actorRouteBuilder.Build());
|
||||
next(app);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\..\properties\actions_managed_netstandard.props" />
|
||||
<Import Project="..\..\properties\actions_managed_stylecop.props" />
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<AssemblyName>Microsoft.Actions.Actors.AspNetCore</AssemblyName>
|
||||
<RootNamespace>$(AssemblyName)</RootNamespace>
|
||||
<DocumentationFile>$(OutputPath)\$(AssemblyName).xml</DocumentationFile>
|
||||
|
||||
<!-- Nuget package properties when packed using dotnet pack. -->
|
||||
<NuspecFile>nuspec\Microsoft.Actions.Actors.AspNetCore.nuspec</NuspecFile>
|
||||
<NuspecProperties>version=$(NupkgVersion);Configuration=$(Configuration)</NuspecProperties>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Hosting.Abstractions" Version="2.2.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Routing" Version="2.2.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Microsoft.Actions.Actors\Microsoft.Actions.Actors.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Update="StyleCop.Analyzers" Version="1.1.118">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Update="Resources\SR.Designer.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>SR.resx</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Update="Resources\SR.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>SR.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,118 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors.AspNetCore
|
||||
{
|
||||
using System.Linq;
|
||||
using Microsoft.Actions.Actors.Runtime;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
internal static class RouterBuilderExtensions
|
||||
{
|
||||
public static void AddActionsConfigRoute(this IRouteBuilder routeBuilder)
|
||||
{
|
||||
routeBuilder.MapGet("actions/config", (request, response, routeData) =>
|
||||
{
|
||||
var result = new JObject(
|
||||
new JProperty("entities", new JArray(ActorRuntime.RegisteredActorTypes.Select(actorType => new JValue(actorType)))));
|
||||
|
||||
return response.WriteAsync(result.ToString());
|
||||
});
|
||||
}
|
||||
|
||||
public static void AddGetSupportedActorTypesRoute(this IRouteBuilder routeBuilder)
|
||||
{
|
||||
routeBuilder.MapGet("actors", (request, response, routeData) =>
|
||||
{
|
||||
var result = new JObject(
|
||||
new JProperty("entities", new JArray(ActorRuntime.RegisteredActorTypes.Select(actorType => new JValue(actorType)))));
|
||||
|
||||
return response.WriteAsync(result.ToString());
|
||||
});
|
||||
}
|
||||
|
||||
public static void AddActorActivationRoute(this IRouteBuilder routeBuilder)
|
||||
{
|
||||
routeBuilder.MapPost("actors/{actorTypeName}/{actorId}", (request, response, routeData) =>
|
||||
{
|
||||
var actorTypeName = (string)routeData.Values["actorTypeName"];
|
||||
var actorId = (string)routeData.Values["actorId"];
|
||||
return ActorRuntime.ActivateAsync(actorTypeName, actorId);
|
||||
});
|
||||
}
|
||||
|
||||
public static void AddActorDeactivationRoute(this IRouteBuilder routeBuilder)
|
||||
{
|
||||
routeBuilder.MapDelete("actors/{actorTypeName}/{actorId}", (request, response, routeData) =>
|
||||
{
|
||||
var actorTypeName = (string)routeData.Values["actorTypeName"];
|
||||
var actorId = (string)routeData.Values["actorId"];
|
||||
return ActorRuntime.DeactivateAsync(actorTypeName, actorId);
|
||||
});
|
||||
}
|
||||
|
||||
public static void AddActorMethodRoute(this IRouteBuilder routeBuilder)
|
||||
{
|
||||
routeBuilder.MapPut("actors/{actorTypeName}/{actorId}/method/{methodName}", (request, response, routeData) =>
|
||||
{
|
||||
var actorTypeName = (string)routeData.Values["actorTypeName"];
|
||||
var actorId = (string)routeData.Values["actorId"];
|
||||
var methodName = (string)routeData.Values["methodName"];
|
||||
|
||||
// If Header is present, call is made using Remoting, use Remoting dispatcher.
|
||||
if (request.Headers.ContainsKey(Constants.RequestHeaderName))
|
||||
{
|
||||
var actionsActorheader = request.Headers[Constants.RequestHeaderName];
|
||||
return ActorRuntime.DispatchWithRemotingAsync(actorTypeName, actorId, methodName, actionsActorheader, request.Body)
|
||||
.ContinueWith(async t =>
|
||||
{
|
||||
var result = t.GetAwaiter().GetResult();
|
||||
|
||||
// Item 1 is header , Item 2 is body
|
||||
if (result.Item1 != string.Empty)
|
||||
{
|
||||
// exception case
|
||||
response.Headers.Add(Constants.ErrorResponseHeaderName, result.Item1); // add error header
|
||||
}
|
||||
|
||||
await response.Body.WriteAsync(result.Item2, 0, result.Item2.Length); // add response message body
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
return ActorRuntime.DispatchWithoutRemotingAsync(actorTypeName, actorId, methodName, request.Body, response.Body);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void AddReminderRoute(this IRouteBuilder routeBuilder)
|
||||
{
|
||||
routeBuilder.MapPut("actors/{actorTypeName}/{actorId}/method/remind/{reminderName}", (request, response, routeData) =>
|
||||
{
|
||||
var actorTypeName = (string)routeData.Values["actorTypeName"];
|
||||
var actorId = (string)routeData.Values["actorId"];
|
||||
var reminderName = (string)routeData.Values["reminderName"];
|
||||
|
||||
// read dueTime, period and data from Request Body.
|
||||
return ActorRuntime.FireReminderAsync(actorTypeName, actorId, reminderName, request.Body);
|
||||
});
|
||||
}
|
||||
|
||||
public static void AddTimerRoute(this IRouteBuilder routeBuilder)
|
||||
{
|
||||
routeBuilder.MapPut("actors/{actorTypeName}/{actorId}/method/timer/{timerName}", (request, response, routeData) =>
|
||||
{
|
||||
var actorTypeName = (string)routeData.Values["actorTypeName"];
|
||||
var actorId = (string)routeData.Values["actorId"];
|
||||
var timerName = (string)routeData.Values["timerName"];
|
||||
|
||||
// read dueTime, period and data from Request Body.
|
||||
return ActorRuntime.FireTimerAsync(actorTypeName, actorId, timerName);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors.AspNetCore
|
||||
{
|
||||
using System;
|
||||
using Microsoft.Actions.Actors.Runtime;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
/// <summary>
|
||||
/// Class containing ActionsActor related extension methods for Microsoft.AspNetCore.Hosting.IWebHostBuilder.
|
||||
/// </summary>
|
||||
public static class WebHostBuilderExtensions
|
||||
{
|
||||
private static readonly string SettingName = "UseActionsActors";
|
||||
|
||||
/// <summary>
|
||||
/// Configures the service to use the routes needed by Actions Actor runtime.
|
||||
/// </summary>
|
||||
/// <param name="hostBuilder">The Microsoft.AspNetCore.Hosting.IWebHostBuilder to configure.</param>
|
||||
/// <param name="configureActorRuntime">Adds a delegate to configure Actor runtime..</param>
|
||||
/// <returns>The Microsoft.AspNetCore.Hosting.IWebHostBuilder.</returns>
|
||||
public static IWebHostBuilder UseActionsActors(this IWebHostBuilder hostBuilder, Action<ActorRuntime> configureActorRuntime)
|
||||
{
|
||||
if (hostBuilder == null)
|
||||
{
|
||||
throw new ArgumentNullException("hostBuilder");
|
||||
}
|
||||
|
||||
// Check if 'UseServiceFabricIntegration' has already been called.
|
||||
if (hostBuilder.GetSetting(SettingName) != null && hostBuilder.GetSetting(SettingName).Equals(true.ToString(), StringComparison.Ordinal))
|
||||
{
|
||||
return hostBuilder;
|
||||
}
|
||||
|
||||
configureActorRuntime.Invoke(ActorRuntime.Instance);
|
||||
|
||||
// Set flag to prevent double service configuration
|
||||
hostBuilder.UseSetting(SettingName, true.ToString());
|
||||
|
||||
hostBuilder.ConfigureServices(services =>
|
||||
{
|
||||
// Add routes.
|
||||
services.AddRouting();
|
||||
services.AddSingleton<IStartupFilter>(new ActionsActorSetupFilter());
|
||||
});
|
||||
|
||||
return hostBuilder;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
<?xml version="1.0"?>
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2011/10/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Microsoft.Actions.Actors.AspNetCore</id>
|
||||
<version>$version$</version>
|
||||
<title>Microsoft.Actions.Actors.AspNetCore</title>
|
||||
<authors>Microsoft</authors>
|
||||
<owners>Microsoft,Actions,Actors</owners>
|
||||
<license type="expression">MIT</license>
|
||||
<projectUrl>http://aka.ms/actions</projectUrl>
|
||||
<iconUrl>http://go.microsoft.com/fwlink/?LinkID=288890</iconUrl>
|
||||
<requireLicenseAcceptance>true</requireLicenseAcceptance>
|
||||
<description>This package contains the reference assemblies for developing Actor services using actions and AspNetCore.</description>
|
||||
<summary>This package contains the reference assemblies for developing Actor services using actions.</summary>
|
||||
<copyright>© Microsoft Corporation. All rights reserved.</copyright>
|
||||
<tags>Microsoft Azure Actions Actors</tags>
|
||||
<dependencies>
|
||||
<dependency id="Microsoft.Actions.Actors" version="[$version$]" />
|
||||
<dependency id="Microsoft.AspNetCore.Hosting.Abstractions" version="2.2.0" />
|
||||
<dependency id="Microsoft.AspNetCore.Routing" version="2.2.0" />
|
||||
</dependencies>
|
||||
</metadata>
|
||||
<files>
|
||||
<file src="..\..\..\bin\$Configuration$\Microsoft.Actions.Actors.AspNetCore.dll" target="lib\netstandard2.0" exclude="" />
|
||||
<file src="..\..\..\bin\$Configuration$\Microsoft.Actions.Actors.AspNetCore.xml" target="lib\netstandard2.0" exclude="" />
|
||||
</files>
|
||||
</package>
|
||||
|
|
@ -18,6 +18,106 @@ namespace Microsoft.Actions.Actors
|
|||
/// <summary>
|
||||
/// Requested resource/content/path does not exist.
|
||||
/// </summary>
|
||||
ACTIONS_E_DOES_NOT_EXIST,
|
||||
ERR_DOES_NOT_EXIST,
|
||||
|
||||
/// <summary>
|
||||
/// Error invoking output binding.
|
||||
/// </summary>
|
||||
ERR_INVOKE_OUTPUT_BINDING,
|
||||
|
||||
/// <summary>
|
||||
/// State store is not found.
|
||||
/// </summary>
|
||||
ERR_STATE_STORE_NOT_FOUND,
|
||||
|
||||
/// <summary>
|
||||
/// Error in getting state.
|
||||
/// </summary>
|
||||
ERR_GET_STATE,
|
||||
|
||||
/// <summary>
|
||||
/// Error in deleting state.
|
||||
/// </summary>
|
||||
ERR_DELETE_STATE,
|
||||
|
||||
/// <summary>
|
||||
/// Malformed request.
|
||||
/// </summary>
|
||||
ERR_MALFORMED_REQUEST,
|
||||
|
||||
/// <summary>
|
||||
/// Error in saving state.
|
||||
/// </summary>
|
||||
ERR_SAVE_REQUEST,
|
||||
|
||||
/// <summary>
|
||||
/// Error in direct invocation.
|
||||
/// </summary>
|
||||
ERR_DIRECT_INVOKE,
|
||||
|
||||
/// <summary>
|
||||
/// Error in invocation.
|
||||
/// </summary>
|
||||
ERR_INVOKE,
|
||||
|
||||
/// <summary>
|
||||
/// Error when actor runtime is not found.
|
||||
/// </summary>
|
||||
ERR_ACTOR_RUNTIME_NOT_FOUND,
|
||||
|
||||
/// <summary>
|
||||
/// Error in creating reminder for the actor.
|
||||
/// </summary>
|
||||
ERR_CREATE_REMINDER,
|
||||
|
||||
/// <summary>
|
||||
/// Error in creating timer for the actor.
|
||||
/// </summary>
|
||||
ERR_CREATE_TIMER,
|
||||
|
||||
/// <summary>
|
||||
/// Error in deleting reminder for the actor.
|
||||
/// </summary>
|
||||
ERR_DELETE_REMINDER,
|
||||
|
||||
/// <summary>
|
||||
/// Error while storing actor state transactionally.
|
||||
/// </summary>
|
||||
ERR_ACTOR_STATE_TRANSACTION,
|
||||
|
||||
/// <summary>
|
||||
/// Error in deleting timer for the actor.
|
||||
/// </summary>
|
||||
ERR_DELETE_TIMER,
|
||||
|
||||
/// <summary>
|
||||
/// Error in invoking actor method.
|
||||
/// </summary>
|
||||
ERR_INVOKE_ACTOR,
|
||||
|
||||
/// <summary>
|
||||
/// Error in deserializing http request body.
|
||||
/// </summary>
|
||||
ERR_DESERIALIZE_HTTP_BODY,
|
||||
|
||||
/// <summary>
|
||||
/// Error in getting state for the actor.
|
||||
/// </summary>
|
||||
ERR_ACTOR_GET_STATE,
|
||||
|
||||
/// <summary>
|
||||
/// Error in deleting state for the actor.
|
||||
/// </summary>
|
||||
ERR_ACTOR_DELETE_STATE,
|
||||
|
||||
/// <summary>
|
||||
/// Pub sub not found.
|
||||
/// </summary>
|
||||
ERR_PUB_SUB_NOT_FOUND,
|
||||
|
||||
/// <summary>
|
||||
/// Error in publishig message.
|
||||
/// </summary>
|
||||
ERR_PUBLISH_MESSAGE,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,543 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Security.Authentication;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Actions.Actors.Communication;
|
||||
using Microsoft.Actions.Actors.Resources;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
/// <summary>
|
||||
/// Class to interact with actions runtime over http.
|
||||
/// </summary>
|
||||
internal class ActionsHttpInteractor : IActionsInteractor
|
||||
{
|
||||
private const string ActionsEndpoint = Constants.ActionsDefaultEndpoint;
|
||||
private const string TraceType = "ActionsHttpInteractor";
|
||||
private readonly string actionsPort = Constants.ActionsDefaultPort;
|
||||
private readonly HttpClientHandler innerHandler;
|
||||
private readonly IReadOnlyList<DelegatingHandler> delegateHandlers;
|
||||
private readonly ClientSettings clientSettings;
|
||||
private HttpClient httpClient = null;
|
||||
private bool disposed = false;
|
||||
|
||||
public ActionsHttpInteractor(
|
||||
HttpClientHandler innerHandler = null,
|
||||
ClientSettings clientSettings = null,
|
||||
params DelegatingHandler[] delegateHandlers)
|
||||
{
|
||||
// Get Actions port from Environment Variable if it has been overridden.
|
||||
var actionsPort = Environment.GetEnvironmentVariable(Constants.ActionsPortEnvironmentVariable);
|
||||
if (actionsPort != null)
|
||||
{
|
||||
this.actionsPort = actionsPort;
|
||||
}
|
||||
|
||||
this.innerHandler = innerHandler ?? new HttpClientHandler();
|
||||
this.delegateHandlers = delegateHandlers;
|
||||
this.clientSettings = clientSettings;
|
||||
|
||||
this.httpClient = this.CreateHttpClient();
|
||||
}
|
||||
|
||||
public async Task<byte[]> GetStateAsync(string actorType, string actorId, string keyName, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
var relativeUrl = string.Format(CultureInfo.InvariantCulture, Constants.ActorStateKeyRelativeUrlFormat, actorType, actorId, keyName);
|
||||
var requestId = Guid.NewGuid().ToString();
|
||||
|
||||
HttpRequestMessage RequestFunc()
|
||||
{
|
||||
var request = new HttpRequestMessage()
|
||||
{
|
||||
Method = HttpMethod.Get,
|
||||
};
|
||||
return request;
|
||||
}
|
||||
|
||||
var response = await this.SendAsync(RequestFunc, relativeUrl, requestId, cancellationToken);
|
||||
var bytes = await response.Content.ReadAsByteArrayAsync();
|
||||
return bytes;
|
||||
}
|
||||
|
||||
public async Task SaveStateAsync(string actorType, string actorId, string keyName, string data, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
var relativeUrl = string.Format(CultureInfo.InvariantCulture, Constants.ActorStateKeyRelativeUrlFormat, actorType, actorId, keyName);
|
||||
var requestId = Guid.NewGuid().ToString();
|
||||
|
||||
HttpRequestMessage RequestFunc()
|
||||
{
|
||||
var request = new HttpRequestMessage()
|
||||
{
|
||||
Method = HttpMethod.Put,
|
||||
Content = new StringContent(data),
|
||||
};
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
var response = await this.SendAsync(RequestFunc, relativeUrl, requestId, cancellationToken);
|
||||
}
|
||||
|
||||
public async Task RemoveStateAsync(string actorType, string actorId, string keyName, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
var relativeUrl = string.Format(CultureInfo.InvariantCulture, Constants.ActorStateKeyRelativeUrlFormat, actorType, actorId, keyName);
|
||||
var requestId = Guid.NewGuid().ToString();
|
||||
|
||||
HttpRequestMessage RequestFunc()
|
||||
{
|
||||
var request = new HttpRequestMessage()
|
||||
{
|
||||
Method = HttpMethod.Delete,
|
||||
};
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
var response = await this.SendAsync(RequestFunc, relativeUrl, requestId, cancellationToken);
|
||||
}
|
||||
|
||||
public Task SaveStateTransactionallyAsync(string actorType, string actorId, string data, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
var relativeUrl = string.Format(CultureInfo.InvariantCulture, Constants.ActorStateRelativeUrlFormat, actorType, actorId);
|
||||
var requestId = Guid.NewGuid().ToString();
|
||||
|
||||
HttpRequestMessage RequestFunc()
|
||||
{
|
||||
var request = new HttpRequestMessage()
|
||||
{
|
||||
Method = HttpMethod.Put,
|
||||
Content = new StringContent(data),
|
||||
};
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
return this.SendAsync(RequestFunc, relativeUrl, requestId, cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<IActorResponseMessage> InvokeActorMethodWithRemotingAsync(ActorMessageSerializersManager serializersManager, IActorRequestMessage remotingRequestRequestMessage, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
var requestMessageHeader = remotingRequestRequestMessage.GetHeader();
|
||||
|
||||
var actorId = requestMessageHeader.ActorId.ToString();
|
||||
var methodName = requestMessageHeader.MethodName;
|
||||
var actorType = requestMessageHeader.ActorType;
|
||||
var interfaceId = requestMessageHeader.InterfaceId;
|
||||
|
||||
var serializedHeader = serializersManager.GetHeaderSerializer()
|
||||
.SerializeRequestHeader(remotingRequestRequestMessage.GetHeader());
|
||||
|
||||
var msgBodySeriaizer = serializersManager.GetRequestMessageBodySerializer(interfaceId);
|
||||
var serializedMsgBody = msgBodySeriaizer.Serialize(remotingRequestRequestMessage.GetBody());
|
||||
|
||||
// Send Request
|
||||
var relativeUrl = string.Format(CultureInfo.InvariantCulture, Constants.ActorMethodRelativeUrlFormat, actorType, actorId, methodName);
|
||||
var requestId = Guid.NewGuid().ToString();
|
||||
|
||||
HttpRequestMessage RequestFunc()
|
||||
{
|
||||
var request = new HttpRequestMessage()
|
||||
{
|
||||
Method = HttpMethod.Put,
|
||||
};
|
||||
|
||||
if (serializedMsgBody != null)
|
||||
{
|
||||
request.Content = new ByteArrayContent(serializedMsgBody);
|
||||
request.Content.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/octet-stream; charset=utf-8");
|
||||
}
|
||||
|
||||
request.Headers.Add(Constants.RequestHeaderName, Encoding.UTF8.GetString(serializedHeader, 0, serializedHeader.Length));
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
var retval = await this.SendAsync(RequestFunc, relativeUrl, requestId, cancellationToken);
|
||||
|
||||
IActorResponseMessageHeader actorResponseMessageHeader = null;
|
||||
if (retval != null && retval.Headers != null)
|
||||
{
|
||||
if (retval.Headers.TryGetValues(Constants.ErrorResponseHeaderName, out IEnumerable<string> headerValues))
|
||||
{
|
||||
var header = headerValues.First();
|
||||
|
||||
// DeSerialize Actor Response Message Header
|
||||
actorResponseMessageHeader =
|
||||
serializersManager.GetHeaderSerializer()
|
||||
.DeserializeResponseHeaders(
|
||||
new MemoryStream(Encoding.ASCII.GetBytes(header)));
|
||||
}
|
||||
}
|
||||
|
||||
// Get the http response message body content and extract out expected actor response message body
|
||||
IActorResponseMessageBody actorResponseMessageBody = null;
|
||||
if (retval != null && retval.Content != null)
|
||||
{
|
||||
var responseMessageBody = await retval.Content.ReadAsStreamAsync();
|
||||
|
||||
// Deserialize Actor Response Message Body
|
||||
// Deserialize to RemoteException when there is response header otherwise normal path
|
||||
var responseBodySerializer = serializersManager.GetResponseMessageBodySerializer(interfaceId);
|
||||
|
||||
// actorResponseMessageHeader is not null, it means there is remote exception
|
||||
if (actorResponseMessageHeader != null)
|
||||
{
|
||||
var isDeserialzied =
|
||||
RemoteException.ToException(
|
||||
responseMessageBody,
|
||||
out var e);
|
||||
if (isDeserialzied)
|
||||
{
|
||||
throw new AggregateException(e);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ServiceException(e.GetType().FullName, string.Format(
|
||||
CultureInfo.InvariantCulture,
|
||||
SR.ErrorDeserializationFailure,
|
||||
e.ToString()));
|
||||
}
|
||||
}
|
||||
|
||||
actorResponseMessageBody = responseBodySerializer.Deserialize(responseMessageBody);
|
||||
}
|
||||
|
||||
return new ActorResponseMessage(actorResponseMessageHeader, actorResponseMessageBody);
|
||||
}
|
||||
|
||||
public async Task<Stream> InvokeActorMethodWithoutRemotingAsync(string actorType, string actorId, string methodName, string jsonPayload, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
var relativeUrl = string.Format(CultureInfo.InvariantCulture, Constants.ActorMethodRelativeUrlFormat, actorType, actorId, methodName);
|
||||
var requestId = Guid.NewGuid().ToString();
|
||||
|
||||
HttpRequestMessage RequestFunc()
|
||||
{
|
||||
var request = new HttpRequestMessage()
|
||||
{
|
||||
Method = HttpMethod.Put,
|
||||
};
|
||||
|
||||
if (jsonPayload != null)
|
||||
{
|
||||
request.Content = new StringContent(jsonPayload);
|
||||
request.Content.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json; charset=utf-8");
|
||||
}
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
var response = await this.SendAsync(RequestFunc, relativeUrl, requestId, cancellationToken);
|
||||
var byteArray = await response.Content.ReadAsStreamAsync();
|
||||
return byteArray;
|
||||
}
|
||||
|
||||
public Task RegisterReminderAsync(string actorType, string actorId, string reminderName, string data, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
var relativeUrl = string.Format(CultureInfo.InvariantCulture, Constants.ActorReminderRelativeUrlFormat, actorType, actorId, reminderName);
|
||||
var requestId = Guid.NewGuid().ToString();
|
||||
|
||||
HttpRequestMessage RequestFunc()
|
||||
{
|
||||
var request = new HttpRequestMessage()
|
||||
{
|
||||
Method = HttpMethod.Put,
|
||||
Content = new StringContent(data, Encoding.UTF8),
|
||||
};
|
||||
|
||||
request.Content.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json; charset=utf-8");
|
||||
return request;
|
||||
}
|
||||
|
||||
return this.SendAsync(RequestFunc, relativeUrl, requestId, cancellationToken);
|
||||
}
|
||||
|
||||
public Task UnregisterReminderAsync(string actorType, string actorId, string reminderName, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
var relativeUrl = string.Format(CultureInfo.InvariantCulture, Constants.ActorReminderRelativeUrlFormat, actorType, actorId, reminderName);
|
||||
var requestId = Guid.NewGuid().ToString();
|
||||
|
||||
HttpRequestMessage RequestFunc()
|
||||
{
|
||||
var request = new HttpRequestMessage()
|
||||
{
|
||||
Method = HttpMethod.Delete,
|
||||
};
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
return this.SendAsync(RequestFunc, relativeUrl, requestId, cancellationToken);
|
||||
}
|
||||
|
||||
public Task RegisterTimerAsync(string actorType, string actorId, string timerName, string data, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
var relativeUrl = string.Format(CultureInfo.InvariantCulture, Constants.ActorTimerRelativeUrlFormat, actorType, actorId, timerName);
|
||||
var requestId = Guid.NewGuid().ToString();
|
||||
|
||||
HttpRequestMessage RequestFunc()
|
||||
{
|
||||
var request = new HttpRequestMessage()
|
||||
{
|
||||
Method = HttpMethod.Put,
|
||||
Content = new StringContent(data, Encoding.UTF8),
|
||||
};
|
||||
|
||||
request.Content.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json; charset=utf-8");
|
||||
return request;
|
||||
}
|
||||
|
||||
return this.SendAsync(RequestFunc, relativeUrl, requestId, cancellationToken);
|
||||
}
|
||||
|
||||
public Task UnregisterTimerAsync(string actorType, string actorId, string timerName, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
var relativeUrl = string.Format(CultureInfo.InvariantCulture, Constants.Timers, actorType, actorId, timerName);
|
||||
var requestId = Guid.NewGuid().ToString();
|
||||
|
||||
HttpRequestMessage RequestFunc()
|
||||
{
|
||||
var request = new HttpRequestMessage()
|
||||
{
|
||||
Method = HttpMethod.Delete,
|
||||
};
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
return this.SendAsync(RequestFunc, relativeUrl, requestId, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends an HTTP get request to Actions.
|
||||
/// </summary>
|
||||
/// <param name="requestFunc">Func to create HttpRequest to send.</param>
|
||||
/// <param name="relativeUri">The relative URI.</param>
|
||||
/// <param name="requestId">Request Id for corelation.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>The payload of the GET response.</returns>
|
||||
internal async Task<HttpResponseMessage> SendAsync(
|
||||
Func<HttpRequestMessage> requestFunc,
|
||||
string relativeUri,
|
||||
string requestId,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
return await this.SendAsyncHandleUnsuccessfulResponse(requestFunc, relativeUri, requestId, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends an HTTP get request to Actions and returns the result as raw json.
|
||||
/// </summary>
|
||||
/// <param name="requestFunc">Func to create HttpRequest to send.</param>
|
||||
/// <param name="relativeUri">The relative URI.</param>
|
||||
/// <param name="requestId">Request Id for corelation.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>The payload of the GET response as string.</returns>
|
||||
internal async Task<string> SendAsyncGetResponseAsRawJson(
|
||||
Func<HttpRequestMessage> requestFunc,
|
||||
string relativeUri,
|
||||
string requestId,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var response = await this.SendAsyncHandleUnsuccessfulResponse(requestFunc, relativeUri, requestId, cancellationToken);
|
||||
var retValue = default(string);
|
||||
|
||||
if (response != null && response.Content != null)
|
||||
{
|
||||
retValue = await response.Content.ReadAsStringAsync();
|
||||
}
|
||||
|
||||
return retValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes resources.
|
||||
/// </summary>
|
||||
/// <param name="disposing">False values indicates the method is being called by the runtime, true value indicates the method is called by the user code.</param>
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!this.disposed)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
this.httpClient.Dispose();
|
||||
this.httpClient = null;
|
||||
}
|
||||
|
||||
this.disposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
private static ActorMessageSerializersManager IntializeSerializationManager(
|
||||
IActorMessageBodySerializationProvider serializationProvider)
|
||||
{
|
||||
// TODO serializer settings
|
||||
return new ActorMessageSerializersManager(
|
||||
serializationProvider,
|
||||
new ActorMessageHeaderSerializer());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends an HTTP get request to cluster http gateway.
|
||||
/// </summary>
|
||||
/// <param name="requestFunc">Func to create HttpRequest to send.</param>
|
||||
/// <param name="relativeUri">The relative URI.</param>
|
||||
/// <param name="requestId">Request Id for corelation.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>The payload of the GET response.</returns>
|
||||
private async Task<HttpResponseMessage> SendAsyncHandleUnsuccessfulResponse(
|
||||
Func<HttpRequestMessage> requestFunc,
|
||||
string relativeUri,
|
||||
string requestId,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
HttpRequestMessage FinalRequestFunc()
|
||||
{
|
||||
var request = requestFunc.Invoke();
|
||||
request.RequestUri = new Uri($"http://{ActionsEndpoint}:{this.actionsPort}/{relativeUri}");
|
||||
|
||||
// Add correlation IDs.
|
||||
request.Headers.Add(Constants.RequestIdHeaderName, this.GetClientRequestIdWithCorrelation(requestId));
|
||||
return request;
|
||||
}
|
||||
|
||||
var response = await this.SendAsyncHandleSecurityExceptions(FinalRequestFunc, cancellationToken);
|
||||
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
return response;
|
||||
}
|
||||
|
||||
// TODO Log Unsuccessful Response.
|
||||
|
||||
// Try to get Error Information if present in response body.
|
||||
if (response.Content != null)
|
||||
{
|
||||
ActionsError error = null;
|
||||
|
||||
try
|
||||
{
|
||||
var contentStream = await response.Content.ReadAsStreamAsync();
|
||||
if (contentStream.Length != 0)
|
||||
{
|
||||
using (var streamReader = new StreamReader(contentStream))
|
||||
{
|
||||
var json = await streamReader.ReadToEndAsync();
|
||||
error = JsonConvert.DeserializeObject<ActionsError>(json);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new ActionsException(string.Format("ServerErrorNoMeaningFulResponse", response.StatusCode), ex);
|
||||
}
|
||||
|
||||
if (error != null)
|
||||
{
|
||||
throw new ActionsException(error.Message, error.ErrorCode ?? ActionsErrorCodes.UNKNOWN, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Handle NotFound 404, without any ErrorCode.
|
||||
if (response.StatusCode.Equals(HttpStatusCode.NotFound))
|
||||
{
|
||||
throw new ActionsException("ErrorMessageHTTP404", ActionsErrorCodes.ERR_DOES_NOT_EXIST, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Couldn't determine Error information from response., throw exception with status code.
|
||||
throw new ActionsException(string.Format("ServerErrorNoMeaningFulResponse", response.StatusCode));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send an HTTP request as an asynchronous operation using HttpClient and handles security exceptions.
|
||||
/// If UserCode to Actions calls are over https in future, this method will handle refreshing security.
|
||||
/// </summary>
|
||||
/// <param name="requestFunc">Delegate to get HTTP request message to send.</param>
|
||||
/// <param name="cancellationToken">The cancellation token to cancel operation.</param>
|
||||
/// <returns>The task object representing the asynchronous operation.</returns>
|
||||
private async Task<HttpResponseMessage> SendAsyncHandleSecurityExceptions(
|
||||
Func<HttpRequestMessage> requestFunc,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
HttpResponseMessage response = null;
|
||||
|
||||
try
|
||||
{
|
||||
// Get the request using the Func as same request cannot be resent when retries are implemented.
|
||||
var request = requestFunc.Invoke();
|
||||
response = await this.httpClient.SendAsync(request, cancellationToken);
|
||||
}
|
||||
catch (AuthenticationException ex)
|
||||
{
|
||||
ActorTrace.Instance.WriteError(TraceType, ex.ToString());
|
||||
throw;
|
||||
}
|
||||
catch (HttpRequestException ex)
|
||||
{
|
||||
ActorTrace.Instance.WriteError(TraceType, ex.ToString());
|
||||
throw;
|
||||
}
|
||||
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
// RefreshSecurity Settings and try again,
|
||||
if (response.StatusCode == HttpStatusCode.Forbidden)
|
||||
{
|
||||
// TODO Log
|
||||
throw new AuthenticationException("Invalid client credentials");
|
||||
}
|
||||
else
|
||||
{
|
||||
return response;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
private string GetClientRequestIdWithCorrelation(string requestId)
|
||||
{
|
||||
// TODO: Add external correlations for tracing.
|
||||
return requestId;
|
||||
}
|
||||
|
||||
private HttpClient CreateHttpClient()
|
||||
{
|
||||
// Chain Delegating Handlers.
|
||||
HttpMessageHandler pipeline = this.innerHandler;
|
||||
if (this.delegateHandlers != null)
|
||||
{
|
||||
for (var i = this.delegateHandlers.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var handler = this.delegateHandlers[i];
|
||||
handler.InnerHandler = pipeline;
|
||||
pipeline = handler;
|
||||
}
|
||||
}
|
||||
|
||||
var httpClientInstance = new HttpClient(pipeline, true);
|
||||
if (this.clientSettings?.ClientTimeout != null)
|
||||
{
|
||||
httpClientInstance.Timeout = (TimeSpan)this.clientSettings.ClientTimeout;
|
||||
}
|
||||
|
||||
return httpClientInstance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -7,37 +7,17 @@ namespace Microsoft.Actions.Actors
|
|||
{
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
/// <summary>
|
||||
/// The ActorId represents the identity of an actor within an actor service.
|
||||
/// </summary>
|
||||
[DataContract(Name = "ActorId")]
|
||||
public class ActorId
|
||||
{
|
||||
private readonly long longId;
|
||||
private readonly Guid guidId;
|
||||
private static readonly Random Rand = new Random();
|
||||
private static readonly object RandLock = new object();
|
||||
private readonly string stringId;
|
||||
private volatile string stringRepresentation;
|
||||
private volatile string storageKey;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ActorId"/> class with Id value of type <see cref="long"/>.
|
||||
/// </summary>
|
||||
/// <param name="id">Value for actor id.</param>
|
||||
public ActorId(long id)
|
||||
{
|
||||
this.Kind = ActorIdKind.Long;
|
||||
this.longId = id;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ActorId"/> class with Id value of type <see cref="System.Guid"/>.
|
||||
/// </summary>
|
||||
/// <param name="id">Value for actor id.</param>
|
||||
public ActorId(Guid id)
|
||||
{
|
||||
this.Kind = ActorIdKind.Guid;
|
||||
this.guidId = id;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ActorId"/> class with Id value of type <see cref="string"/>.
|
||||
|
|
@ -45,20 +25,67 @@ namespace Microsoft.Actions.Actors
|
|||
/// <param name="id">Value for actor id.</param>
|
||||
public ActorId(string id)
|
||||
{
|
||||
if (id == null)
|
||||
{
|
||||
throw new ArgumentNullException("id");
|
||||
}
|
||||
|
||||
this.Kind = ActorIdKind.String;
|
||||
this.stringId = id;
|
||||
this.stringId = id ?? throw new ArgumentNullException("id");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="ActorIdKind"/> for the ActorId.
|
||||
/// Determines whether two specified actorIds have the same id.
|
||||
/// </summary>
|
||||
/// <value><see cref="ActorIdKind"/> for the ActorId.</value>
|
||||
public ActorIdKind Kind { get; }
|
||||
/// <param name="x">The first actorId to compare, or null. </param>
|
||||
/// <param name="y">The second actorId to compare, or null. </param>
|
||||
/// <returns>true if the id is same for both objects; otherwise, false.</returns>
|
||||
public static bool operator ==(ActorId x, ActorId y)
|
||||
{
|
||||
if (ReferenceEquals(x, null) && ReferenceEquals(y, null))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (ReferenceEquals(x, null) || ReferenceEquals(y, null))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return EqualsContents(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether two specified actorIds have different values for id./>.
|
||||
/// </summary>
|
||||
/// <param name="x">The first actorId to compare, or null. </param>
|
||||
/// <param name="y">The second actorId to compare, or null. </param>
|
||||
/// <returns>true if the id is different for both objects; otherwise, true.</returns>
|
||||
public static bool operator !=(ActorId x, ActorId y)
|
||||
{
|
||||
return !(x == y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new instance of the <see cref="ActorId"/>./>
|
||||
/// with a random <see cref="long"/> id value.
|
||||
/// </summary>
|
||||
/// <returns>A new ActorId object.</returns>
|
||||
/// <remarks>This method is thread-safe and generates a new random <see cref="ActorId"/> every time it is called.</remarks>
|
||||
public static ActorId CreateRandom()
|
||||
{
|
||||
var buffer = new byte[8];
|
||||
lock (RandLock)
|
||||
{
|
||||
Rand.NextBytes(buffer);
|
||||
}
|
||||
|
||||
return new ActorId(BitConverter.ToString(buffer, 0));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets id./>.
|
||||
/// </summary>
|
||||
/// <returns><see cref="string"/>The id value for ActorId.</returns>
|
||||
public string GetId()
|
||||
{
|
||||
return this.stringId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides <see cref="object.ToString"/>.
|
||||
|
|
@ -66,69 +93,81 @@ namespace Microsoft.Actions.Actors
|
|||
/// <returns>Returns a string that represents the current object.</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
if (this.stringRepresentation != null)
|
||||
{
|
||||
return this.stringRepresentation;
|
||||
}
|
||||
|
||||
var actorIdAsString = string.Empty;
|
||||
switch (this.Kind)
|
||||
{
|
||||
case ActorIdKind.Long:
|
||||
actorIdAsString = this.longId.ToString(CultureInfo.InvariantCulture);
|
||||
break;
|
||||
|
||||
case ActorIdKind.Guid:
|
||||
actorIdAsString = this.guidId.ToString();
|
||||
break;
|
||||
|
||||
case ActorIdKind.String:
|
||||
actorIdAsString = this.stringId;
|
||||
break;
|
||||
|
||||
default:
|
||||
Environment.FailFast($"The ActorIdKind value {this.Kind} is invalid");
|
||||
break;
|
||||
}
|
||||
|
||||
this.stringRepresentation = actorIdAsString;
|
||||
return actorIdAsString;
|
||||
return this.stringId;
|
||||
}
|
||||
|
||||
internal string GetStorageKey()
|
||||
/// <summary>
|
||||
/// Overrides <see cref="object.GetHashCode"/>.
|
||||
/// </summary>
|
||||
/// <returns>Hash code for the current object.</returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
if (this.storageKey == null)
|
||||
return this.stringId.GetHashCode();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether this instance and a specified object, which must also be a <see cref="ActorId"/> object,
|
||||
/// have the same value. Overrides <see cref="object.Equals(object)"/>.
|
||||
/// </summary>
|
||||
/// <param name="obj">The actorId to compare to this instance. </param>
|
||||
/// <returns>true if obj is a <see cref="ActorId"/> and its value is the same as this instance;
|
||||
/// otherwise, false. If obj is null, the method returns false.</returns>
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (ReferenceEquals(obj, null))
|
||||
{
|
||||
// Needs InvariantCulture for key.
|
||||
string key;
|
||||
switch (this.Kind)
|
||||
{
|
||||
case ActorIdKind.Long:
|
||||
key = string.Format(CultureInfo.InvariantCulture, "{0}_{1}", this.Kind.ToString(), this.longId);
|
||||
break;
|
||||
|
||||
case ActorIdKind.Guid:
|
||||
key = string.Format(CultureInfo.InvariantCulture, "{0}_{1}", this.Kind.ToString(), this.guidId);
|
||||
break;
|
||||
|
||||
case ActorIdKind.String:
|
||||
key = string.Format(
|
||||
CultureInfo.InvariantCulture,
|
||||
"{0}_{1}",
|
||||
this.Kind.ToString(),
|
||||
this.stringId);
|
||||
break;
|
||||
|
||||
default:
|
||||
Environment.FailFast($"The ActorIdKind value {this.Kind} is invalid");
|
||||
key = null; // unreachable
|
||||
break;
|
||||
}
|
||||
|
||||
this.storageKey = key;
|
||||
return false;
|
||||
}
|
||||
else if (obj.GetType() != typeof(ActorId))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return EqualsContents(this, (ActorId)obj);
|
||||
}
|
||||
}
|
||||
|
||||
return this.storageKey;
|
||||
/// <summary>
|
||||
/// Determines whether this instance and another specified <see cref="ActorId"/> object have the same value.
|
||||
/// </summary>
|
||||
/// <param name="other">The actorId to compare to this instance. </param>
|
||||
/// <returns>true if the id of the other parameter is the same as the
|
||||
/// id of this instance; otherwise, false.
|
||||
/// If other is null, the method returns false.</returns>
|
||||
public bool Equals(ActorId other)
|
||||
{
|
||||
if (ReferenceEquals(other, null))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return EqualsContents(this, other);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares this instance with a specified <see cref="ActorId"/> object and indicates whether this
|
||||
/// instance precedes, follows, or appears in the same position in the sort order as the specified actorId.
|
||||
/// </summary>
|
||||
/// <param name="other">The actorId to compare with this instance. </param>
|
||||
/// <returns>A 32-bit signed integer that indicates whether this instance precedes, follows, or appears
|
||||
/// in the same position in the sort order as the other parameter.</returns>
|
||||
/// <remarks>The comparison is done based on the id if both the instances.</remarks>
|
||||
public int CompareTo(ActorId other)
|
||||
{
|
||||
return ReferenceEquals(other, null) ? 1 : CompareContents(this, other);
|
||||
}
|
||||
|
||||
private static bool EqualsContents(ActorId x, ActorId y)
|
||||
{
|
||||
return string.Equals(x.stringId, y.stringId, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
private static int CompareContents(ActorId x, ActorId y)
|
||||
{
|
||||
return string.Compare(x.stringId, y.stringId, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,28 +0,0 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies the type of the ID value for an <see cref="ActorId"/>.
|
||||
/// </summary>
|
||||
public enum ActorIdKind
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents ID value of type <see cref="long"/>.
|
||||
/// </summary>
|
||||
Long = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Represents ID value of type <see cref="System.Guid"/>.
|
||||
/// </summary>
|
||||
Guid = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Represents ID value of type <see cref="string"/>.
|
||||
/// </summary>
|
||||
String = 2,
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors
|
||||
{
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
using Microsoft.Actions.Actors.Client;
|
||||
|
||||
/// <summary>
|
||||
/// Encapsulation of a reference to an actor for serialization.
|
||||
/// </summary>
|
||||
[DataContract(Name = "ActorReference", Namespace = Constants.Namespace)]
|
||||
[Serializable]
|
||||
public sealed class ActorReference : IActorReference
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ActorReference"/> class.
|
||||
/// </summary>
|
||||
public ActorReference()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="Actions.Actors.ActorId"/> of the actor.
|
||||
/// </summary>
|
||||
/// <value><see cref="Actions.Actors.ActorId"/> of the actor.</value>
|
||||
[DataMember(Name = "ActorId", Order = 0, IsRequired = true)]
|
||||
public ActorId ActorId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the implementation type of the actor.
|
||||
/// </summary>
|
||||
/// <value>Implementation type name of the actor.</value>
|
||||
[DataMember(Name = "ActorType", Order = 0, IsRequired = true)]
|
||||
public string ActorType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets <see cref="ActorReference"/> for the actor.
|
||||
/// </summary>
|
||||
/// <param name="actor">Actor object to get <see cref="ActorReference"/> for.</param>
|
||||
/// <returns><see cref="ActorReference"/> object for the actor.</returns>
|
||||
/// <remarks>A null value is returned if actor is passed as null.</remarks>
|
||||
public static ActorReference Get(object actor)
|
||||
{
|
||||
if (actor != null)
|
||||
{
|
||||
return GetActorReference(actor);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an <see cref="ActorProxy"/> that implements an actor interface for the actor using the
|
||||
/// <see cref="ActorProxyFactory.CreateActorProxy(Microsoft.Actions.Actors.ActorId, System.Type, string)"/>
|
||||
/// method.
|
||||
/// </summary>
|
||||
/// <param name="actorInterfaceType">Actor interface for the created <see cref="ActorProxy"/> to implement.</param>
|
||||
/// <returns>An actor proxy object that implements <see cref="IActorProxy"/> and TActorInterface.</returns>
|
||||
public object Bind(Type actorInterfaceType)
|
||||
{
|
||||
return ActorProxy.DefaultProxyFactory.CreateActorProxy(this.ActorId, actorInterfaceType, this.ActorType);
|
||||
}
|
||||
|
||||
private static ActorReference GetActorReference(object actor)
|
||||
{
|
||||
if (actor == null)
|
||||
{
|
||||
throw new ArgumentNullException("actor");
|
||||
}
|
||||
|
||||
// try as IActorProxy for backward compatibility as customers's mock framework may rely on it before V2 remoting stack.
|
||||
if (actor is IActorProxy actorProxy)
|
||||
{
|
||||
return new ActorReference()
|
||||
{
|
||||
ActorId = actorProxy.ActorId,
|
||||
ActorType = actorProxy.ActorType,
|
||||
};
|
||||
}
|
||||
|
||||
// TODO check for ActorBase
|
||||
throw new ArgumentOutOfRangeException("actor");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,66 +0,0 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors
|
||||
{
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Actions.Actors.Runtime;
|
||||
|
||||
internal sealed class ActorStateManager : IActorStateManager
|
||||
{
|
||||
public Task AddOrUpdateState<T>(string stateName, T value)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task ClearCacheAsync(CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<T> GetStateAsync<T>(string stateName, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public Task RemoveStateAsync(string stateName, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public Task SaveStateAsync(CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
private sealed class StateMetadata
|
||||
{
|
||||
private StateMetadata(object value, Type type, StateChangeKind changeKind)
|
||||
{
|
||||
this.Value = value;
|
||||
this.Type = type;
|
||||
this.ChangeKind = changeKind;
|
||||
}
|
||||
|
||||
public object Value { get; set; }
|
||||
|
||||
public StateChangeKind ChangeKind { get; set; }
|
||||
|
||||
public Type Type { get; }
|
||||
|
||||
public static StateMetadata Create<T>(T value, StateChangeKind changeKind)
|
||||
{
|
||||
return new StateMetadata(value, typeof(T), changeKind);
|
||||
}
|
||||
|
||||
public static StateMetadata CreateForRemove()
|
||||
{
|
||||
return new StateMetadata(null, typeof(object), StateChangeKind.Remove);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5,30 +5,118 @@
|
|||
|
||||
namespace Microsoft.Actions.Actors
|
||||
{
|
||||
using System;
|
||||
using System.Globalization;
|
||||
|
||||
internal sealed class ActorTrace
|
||||
{
|
||||
internal static readonly ActorTrace Instance = new ActorTrace();
|
||||
private readonly ITraceWriter traceWriter;
|
||||
|
||||
/// <summary>
|
||||
/// Prevents a default instance of the <see cref="ActorTrace" /> class from being created.
|
||||
/// </summary>
|
||||
private ActorTrace()
|
||||
{
|
||||
// TODO: Replace with actual TraceWriter (or integrate with distributed tracing).
|
||||
// Use ConsoleTraceWriter during development & test.
|
||||
this.traceWriter = new ConsoleTraceWriter();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Interface for traces.
|
||||
/// </summary>
|
||||
private interface ITraceWriter
|
||||
{
|
||||
/// <summary>
|
||||
/// Writes info trace.
|
||||
/// </summary>
|
||||
/// <param name="infoText">Text to trace.</param>
|
||||
void WriteInfo(string infoText);
|
||||
|
||||
/// <summary>
|
||||
/// Writes warning trace.
|
||||
/// </summary>
|
||||
/// <param name="warningText">Text to trace.</param>
|
||||
void WriteWarning(string warningText);
|
||||
|
||||
/// <summary>
|
||||
/// Writes Error trace.
|
||||
/// </summary>
|
||||
/// <param name="errorText">Text to trace.</param>
|
||||
void WriteError(string errorText);
|
||||
}
|
||||
|
||||
internal void WriteInfo(string type, string format, params object[] args)
|
||||
{
|
||||
this.WriteInfoWithId(type, string.Empty, format, args);
|
||||
}
|
||||
|
||||
internal void WriteInfoWithId(string type, string id, string format, params object[] args)
|
||||
{
|
||||
if (args == null || args.Length == 0)
|
||||
{
|
||||
// Write Informational Trace.
|
||||
// Instance.InfoText(id, type, format);
|
||||
this.traceWriter.WriteInfo($"{type}: {id} {format}");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Write Informational Trace.
|
||||
// Instance.InfoText(id, type, string.Format(CultureInfo.InvariantCulture, format, args));
|
||||
this.traceWriter.WriteInfo($"{type}: {id} {string.Format(CultureInfo.InvariantCulture, format, args)}");
|
||||
}
|
||||
}
|
||||
|
||||
internal void WriteWarning(string type, string format, params object[] args)
|
||||
{
|
||||
this.WriteWarningWithId(type, string.Empty, format, args);
|
||||
}
|
||||
|
||||
internal void WriteWarningWithId(string type, string id, string format, params object[] args)
|
||||
{
|
||||
if (args == null || args.Length == 0)
|
||||
{
|
||||
this.traceWriter.WriteWarning($"{type}: {id} {format}");
|
||||
}
|
||||
else
|
||||
{
|
||||
this.traceWriter.WriteWarning($"{type}: {id} {string.Format(CultureInfo.InvariantCulture, format, args)}");
|
||||
}
|
||||
}
|
||||
|
||||
internal void WriteError(string type, string format, params object[] args)
|
||||
{
|
||||
this.WriteErrorWithId(type, string.Empty, format, args);
|
||||
}
|
||||
|
||||
internal void WriteErrorWithId(string type, string id, string format, params object[] args)
|
||||
{
|
||||
if (args == null || args.Length == 0)
|
||||
{
|
||||
this.traceWriter.WriteError($"{type}: {id} {format}");
|
||||
}
|
||||
else
|
||||
{
|
||||
this.traceWriter.WriteError($"{type}: {id} {string.Format(CultureInfo.InvariantCulture, format, args)}");
|
||||
}
|
||||
}
|
||||
|
||||
private class ConsoleTraceWriter : ITraceWriter
|
||||
{
|
||||
public void WriteError(string errorText)
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.Red;
|
||||
Console.WriteLine($"ERROR: {errorText}");
|
||||
Console.ResetColor();
|
||||
}
|
||||
|
||||
public void WriteInfo(string infoText)
|
||||
{
|
||||
Console.WriteLine(infoText);
|
||||
}
|
||||
|
||||
public void WriteWarning(string warningText)
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.Yellow;
|
||||
Console.WriteLine($"WARNING: {warningText}");
|
||||
Console.ResetColor();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,181 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors.Builder
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.Actions.Actors.Description;
|
||||
using Microsoft.Actions.Actors.Runtime;
|
||||
|
||||
internal class ActorCodeBuilder : ICodeBuilder
|
||||
{
|
||||
internal static readonly InterfaceDetailsStore InterfaceDetailsStore = new InterfaceDetailsStore();
|
||||
private static readonly ICodeBuilder Instance = new ActorCodeBuilder(new ActorCodeBuilderNames("V1"));
|
||||
private static readonly object BuildLock = new object();
|
||||
private readonly MethodBodyTypesBuilder methodBodyTypesBuilder;
|
||||
private readonly MethodDispatcherBuilder<ActorMethodDispatcherBase> methodDispatcherBuilder;
|
||||
private readonly ActorProxyGeneratorBuilder proxyGeneratorBuilder;
|
||||
|
||||
private readonly Dictionary<Type, MethodBodyTypesBuildResult> methodBodyTypesBuildResultMap;
|
||||
private readonly Dictionary<Type, MethodDispatcherBuildResult> methodDispatcherBuildResultMap;
|
||||
private readonly Dictionary<Type, ActorProxyGeneratorBuildResult> proxyGeneratorBuildResultMap;
|
||||
|
||||
private readonly ICodeBuilderNames codeBuilderNames;
|
||||
|
||||
public ActorCodeBuilder(ICodeBuilderNames codeBuilderNames)
|
||||
{
|
||||
this.codeBuilderNames = codeBuilderNames;
|
||||
|
||||
this.methodBodyTypesBuildResultMap = new Dictionary<Type, MethodBodyTypesBuildResult>();
|
||||
this.methodDispatcherBuildResultMap = new Dictionary<Type, MethodDispatcherBuildResult>();
|
||||
this.proxyGeneratorBuildResultMap = new Dictionary<Type, ActorProxyGeneratorBuildResult>();
|
||||
|
||||
this.methodBodyTypesBuilder = new MethodBodyTypesBuilder(this);
|
||||
this.methodDispatcherBuilder = new MethodDispatcherBuilder<ActorMethodDispatcherBase>(this);
|
||||
this.proxyGeneratorBuilder = new ActorProxyGeneratorBuilder(this);
|
||||
}
|
||||
|
||||
ICodeBuilderNames ICodeBuilder.Names
|
||||
{
|
||||
get { return this.codeBuilderNames; }
|
||||
}
|
||||
|
||||
public static ActorProxyGenerator GetOrCreateProxyGenerator(Type actorInterfaceType)
|
||||
{
|
||||
lock (BuildLock)
|
||||
{
|
||||
return (ActorProxyGenerator)Instance.GetOrBuildProxyGenerator(actorInterfaceType).ProxyGenerator;
|
||||
}
|
||||
}
|
||||
|
||||
public static ActorMethodDispatcherBase GetOrCreateMethodDispatcher(Type actorInterfaceType)
|
||||
{
|
||||
lock (BuildLock)
|
||||
{
|
||||
return (ActorMethodDispatcherBase)Instance.GetOrBuilderMethodDispatcher(actorInterfaceType).MethodDispatcher;
|
||||
}
|
||||
}
|
||||
|
||||
MethodDispatcherBuildResult ICodeBuilder.GetOrBuilderMethodDispatcher(Type interfaceType)
|
||||
{
|
||||
if (this.TryGetMethodDispatcher(interfaceType, out var result))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
result = this.BuildMethodDispatcher(interfaceType);
|
||||
this.UpdateMethodDispatcherBuildMap(interfaceType, result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
MethodBodyTypesBuildResult ICodeBuilder.GetOrBuildMethodBodyTypes(Type interfaceType)
|
||||
{
|
||||
if (this.methodBodyTypesBuildResultMap.TryGetValue(interfaceType, out var result))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
result = this.BuildMethodBodyTypes(interfaceType);
|
||||
this.methodBodyTypesBuildResultMap.Add(interfaceType, result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
ActorProxyGeneratorBuildResult ICodeBuilder.GetOrBuildProxyGenerator(Type interfaceType)
|
||||
{
|
||||
if (this.TryGetProxyGenerator(interfaceType, out var result))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
result = this.BuildProxyGenerator(interfaceType);
|
||||
this.UpdateProxyGeneratorMap(interfaceType, result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
internal static bool TryGetKnownTypes(int interfaceId, out InterfaceDetails interfaceDetails)
|
||||
{
|
||||
return InterfaceDetailsStore.TryGetKnownTypes(interfaceId, out interfaceDetails);
|
||||
}
|
||||
|
||||
internal static bool TryGetKnownTypes(string interfaceName, out InterfaceDetails interfaceDetails)
|
||||
{
|
||||
return InterfaceDetailsStore.TryGetKnownTypes(interfaceName, out interfaceDetails);
|
||||
}
|
||||
|
||||
protected MethodDispatcherBuildResult BuildMethodDispatcher(Type interfaceType)
|
||||
{
|
||||
var actorInterfaceDescription = ActorInterfaceDescription.CreateUsingCRCId(interfaceType);
|
||||
var res = this.methodDispatcherBuilder.Build(actorInterfaceDescription);
|
||||
return res;
|
||||
}
|
||||
|
||||
protected MethodBodyTypesBuildResult BuildMethodBodyTypes(Type interfaceType)
|
||||
{
|
||||
var actorInterfaceDescriptions = ActorInterfaceDescription.CreateUsingCRCId(interfaceType);
|
||||
var result = this.methodBodyTypesBuilder.Build(actorInterfaceDescriptions);
|
||||
InterfaceDetailsStore.UpdateKnownTypeDetail(actorInterfaceDescriptions, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
protected ActorProxyGeneratorBuildResult BuildProxyGenerator(Type interfaceType)
|
||||
{
|
||||
// create all actor interfaces that this interface derives from
|
||||
var actorInterfaces = new List<Type>() { interfaceType };
|
||||
actorInterfaces.AddRange(interfaceType.GetActorInterfaces());
|
||||
|
||||
// create interface descriptions for all interfaces
|
||||
var actorInterfaceDescriptions = actorInterfaces.Select<Type, InterfaceDescription>(
|
||||
t => ActorInterfaceDescription.CreateUsingCRCId(t));
|
||||
|
||||
var res = this.proxyGeneratorBuilder.Build(interfaceType, actorInterfaceDescriptions);
|
||||
return res;
|
||||
}
|
||||
|
||||
protected void UpdateMethodDispatcherBuildMap(Type interfaceType, MethodDispatcherBuildResult result)
|
||||
{
|
||||
this.methodDispatcherBuildResultMap.Add(interfaceType, result);
|
||||
}
|
||||
|
||||
protected bool TryGetMethodDispatcher(
|
||||
Type interfaceType,
|
||||
out MethodDispatcherBuildResult builderMethodDispatcher)
|
||||
{
|
||||
if (this.methodDispatcherBuildResultMap.TryGetValue(interfaceType, out var result))
|
||||
{
|
||||
{
|
||||
builderMethodDispatcher = result;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
builderMethodDispatcher = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
protected void UpdateProxyGeneratorMap(Type interfaceType, ActorProxyGeneratorBuildResult result)
|
||||
{
|
||||
this.proxyGeneratorBuildResultMap.Add(interfaceType, result);
|
||||
}
|
||||
|
||||
protected bool TryGetProxyGenerator(Type interfaceType, out ActorProxyGeneratorBuildResult orBuildProxyGenerator)
|
||||
{
|
||||
if (this.proxyGeneratorBuildResultMap.TryGetValue(interfaceType, out var result))
|
||||
{
|
||||
{
|
||||
orBuildProxyGenerator = result;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
orBuildProxyGenerator = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,105 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors.Builder
|
||||
{
|
||||
using System;
|
||||
using System.Globalization;
|
||||
|
||||
internal class ActorCodeBuilderNames : ICodeBuilderNames
|
||||
{
|
||||
private readonly string namePrefix;
|
||||
|
||||
public ActorCodeBuilderNames()
|
||||
: this("actor")
|
||||
{
|
||||
}
|
||||
|
||||
public ActorCodeBuilderNames(string namePrefix)
|
||||
{
|
||||
this.namePrefix = "actor" + namePrefix;
|
||||
}
|
||||
|
||||
public string InterfaceId
|
||||
{
|
||||
get { return "interfaceId"; }
|
||||
}
|
||||
|
||||
public string MethodId
|
||||
{
|
||||
get { return "methodId"; }
|
||||
}
|
||||
|
||||
public string RetVal
|
||||
{
|
||||
get { return "retVal"; }
|
||||
}
|
||||
|
||||
public string RequestBody
|
||||
{
|
||||
get { return "requestBody"; }
|
||||
}
|
||||
|
||||
public string GetMethodBodyTypesAssemblyName(Type interfaceType)
|
||||
{
|
||||
return string.Format(CultureInfo.InvariantCulture, "{0}_.{1}.mt", interfaceType.FullName, this.namePrefix);
|
||||
}
|
||||
|
||||
public string GetMethodBodyTypesAssemblyNamespace(Type interfaceType)
|
||||
{
|
||||
return string.Format(CultureInfo.InvariantCulture, "{0}_.{1}.mt", interfaceType.FullName, this.namePrefix);
|
||||
}
|
||||
|
||||
public string GetRequestBodyTypeName(string methodName)
|
||||
{
|
||||
return string.Format(CultureInfo.InvariantCulture, "{0}ReqBody", methodName);
|
||||
}
|
||||
|
||||
public string GetResponseBodyTypeName(string methodName)
|
||||
{
|
||||
return string.Format(CultureInfo.InvariantCulture, "{0}RespBody", methodName);
|
||||
}
|
||||
|
||||
public string GetMethodDispatcherAssemblyName(Type interfaceType)
|
||||
{
|
||||
return string.Format(CultureInfo.InvariantCulture, "{0}_.{1}.disp", interfaceType.FullName, this.namePrefix);
|
||||
}
|
||||
|
||||
public string GetMethodDispatcherAssemblyNamespace(Type interfaceType)
|
||||
{
|
||||
return string.Format(CultureInfo.InvariantCulture, "{0}_.{1}.disp", interfaceType.FullName, this.namePrefix);
|
||||
}
|
||||
|
||||
public string GetMethodDispatcherClassName(Type interfaceType)
|
||||
{
|
||||
return string.Format(CultureInfo.InvariantCulture, "{0}MethodDispatcher", interfaceType.Name);
|
||||
}
|
||||
|
||||
public string GetProxyAssemblyName(Type interfaceType)
|
||||
{
|
||||
return string.Format(CultureInfo.InvariantCulture, "{0}_.{1}.proxy", interfaceType.FullName, this.namePrefix);
|
||||
}
|
||||
|
||||
public string GetProxyAssemblyNamespace(Type interfaceType)
|
||||
{
|
||||
return string.Format(CultureInfo.InvariantCulture, "{0}_.{1}.proxy", interfaceType.FullName, this.namePrefix);
|
||||
}
|
||||
|
||||
public string GetProxyClassName(Type interfaceType)
|
||||
{
|
||||
return string.Format(CultureInfo.InvariantCulture, "{0}{1}Proxy", interfaceType.Name, this.namePrefix);
|
||||
}
|
||||
|
||||
public string GetProxyActivatorClassName(Type interfaceType)
|
||||
{
|
||||
return string.Format(CultureInfo.InvariantCulture, "{0}{1}ProxyActivator", interfaceType.Name, this.namePrefix);
|
||||
}
|
||||
|
||||
public string GetDataContractNamespace()
|
||||
{
|
||||
return Constants.Namespace;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,238 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors.Builder
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Actions.Actors.Communication;
|
||||
using Microsoft.Actions.Actors.Description;
|
||||
using Microsoft.Actions.Actors.Resources;
|
||||
|
||||
/// <summary>
|
||||
/// The class is used by actor remoting code generator to generate a type that dispatches requests to actor
|
||||
/// object by invoking right method on it.
|
||||
/// </summary>
|
||||
public abstract class ActorMethodDispatcherBase
|
||||
{
|
||||
private IReadOnlyDictionary<int, string> methodNameMap;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the id of the interface supported by this method dispatcher.
|
||||
/// </summary>
|
||||
public int InterfaceId { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Why we pass IMessageBodyFactory to this function instead of
|
||||
/// setting at class level?. Since we cache MethodDispatcher for each interface,
|
||||
/// we can't set IMessageBodyFactory at class level.
|
||||
/// These can be cases where multiple IMessageBodyFactory implmenetation but single dispatcher class.
|
||||
/// This method is used to dispatch request to the specified methodId of the
|
||||
/// interface implemented by the remoted object.
|
||||
/// </summary>
|
||||
/// <param name="objectImplementation">The object impplemented the remoted interface.</param>
|
||||
/// <param name="methodId">Id of the method to which to dispatch the request to.</param>
|
||||
/// <param name="requestBody">The body of the request object that needs to be dispatched to the object.</param>
|
||||
/// <param name="remotingMessageBodyFactory">IMessageBodyFactory implementaion.</param>
|
||||
/// <param name="cancellationToken">The cancellation token that will be signaled if this operation is cancelled.</param>
|
||||
/// <returns>A task that represents the outstanding asynchronous call to the implementation object.
|
||||
/// The return value of the task contains the returned value from the invoked method.</returns>
|
||||
public Task<IActorResponseMessageBody> DispatchAsync(
|
||||
object objectImplementation,
|
||||
int methodId,
|
||||
IActorRequestMessageBody requestBody,
|
||||
IActorMessageBodyFactory remotingMessageBodyFactory,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
var dispatchTask = this.OnDispatchAsync(
|
||||
methodId,
|
||||
objectImplementation,
|
||||
requestBody,
|
||||
remotingMessageBodyFactory,
|
||||
cancellationToken);
|
||||
|
||||
return dispatchTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method is used to dispatch one way messages to the specified methodId of the
|
||||
/// interface implemented by the remoted object.
|
||||
/// </summary>
|
||||
/// <param name="objectImplementation">The object implemented the remoted interface.</param>
|
||||
/// <param name="methodId">Id of the method to which to dispatch the request to.</param>
|
||||
/// <param name="requestMessageBody">The body of the request object that needs to be dispatched to the remoting implementation.</param>
|
||||
public void Dispatch(object objectImplementation, int methodId, IActorRequestMessageBody requestMessageBody)
|
||||
{
|
||||
this.OnDispatch(methodId, objectImplementation, requestMessageBody);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the method that has the specified methodId.
|
||||
/// </summary>
|
||||
/// <param name="methodId">The id of the method.</param>
|
||||
/// <returns>The name of the method corresponding to the specified method id.</returns>
|
||||
public string GetMethodName(int methodId)
|
||||
{
|
||||
if (!this.methodNameMap.TryGetValue(methodId, out var methodName))
|
||||
{
|
||||
throw new MissingMethodException(string.Format(
|
||||
CultureInfo.CurrentCulture,
|
||||
SR.ErrorMissingMethod,
|
||||
methodId,
|
||||
this.InterfaceId));
|
||||
}
|
||||
|
||||
return methodName;
|
||||
}
|
||||
|
||||
internal void Initialize(InterfaceDescription description, IReadOnlyDictionary<int, string> methodMap)
|
||||
{
|
||||
this.SetInterfaceId(description.Id);
|
||||
this.SetMethodNameMap(methodMap);
|
||||
}
|
||||
|
||||
internal void SetInterfaceId(int interfaceId)
|
||||
{
|
||||
this.InterfaceId = interfaceId;
|
||||
}
|
||||
|
||||
internal void SetMethodNameMap(IReadOnlyDictionary<int, string> methodNameMap)
|
||||
{
|
||||
this.methodNameMap = methodNameMap;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method is used to create the remoting response from the specified return value.
|
||||
/// </summary>
|
||||
/// <param name="interfaceName">Interface Name of the remoting Interface.</param>
|
||||
/// <param name="methodName">Method Name of the remoting method.</param>
|
||||
/// <param name="methodId">MethodId of the remoting method.</param>
|
||||
/// <param name="remotingMessageBodyFactory">MessageFactory for the remoting Interface.</param>
|
||||
/// <param name="response">Response returned by remoting method.</param>
|
||||
/// <returns>Actor Response Message Body.</returns>
|
||||
protected IActorResponseMessageBody CreateResponseMessageBody(
|
||||
string interfaceName,
|
||||
string methodName,
|
||||
int methodId,
|
||||
IActorMessageBodyFactory remotingMessageBodyFactory,
|
||||
object response)
|
||||
{
|
||||
var msg = remotingMessageBodyFactory.CreateResponseMessageBody(
|
||||
interfaceName,
|
||||
methodName,
|
||||
this.CreateWrappedResponseBody(methodId, response));
|
||||
|
||||
if (!(msg is WrappedMessage))
|
||||
{
|
||||
msg.Set(response);
|
||||
}
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method is implemented by the generated method dispatcher to dispatch request to the specified methodId of the
|
||||
/// interface implemented by the remoted object.
|
||||
/// </summary>
|
||||
/// <param name="methodId">Id of the method.</param>
|
||||
/// <param name="remotedObject">The remoted object instance.</param>
|
||||
/// <param name="requestBody">Request body.</param>
|
||||
/// <param name="remotingMessageBodyFactory">Remoting Message Body Factory implementation needed for creating response object.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>
|
||||
/// A <see cref="System.Threading.Tasks.Task">Task</see> that represents outstanding operation.
|
||||
/// The result of the task is the return value from the method.
|
||||
/// </returns>
|
||||
protected abstract Task<IActorResponseMessageBody> OnDispatchAsync(
|
||||
int methodId,
|
||||
object remotedObject,
|
||||
IActorRequestMessageBody requestBody,
|
||||
IActorMessageBodyFactory remotingMessageBodyFactory,
|
||||
CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// This method is implemented by the generated method dispatcher to dispatch one way messages to the specified methodId of the
|
||||
/// interface implemented by the remoted object.
|
||||
/// </summary>
|
||||
/// <param name="methodId">Id of the method.</param>
|
||||
/// <param name="remotedObject">The remoted object instance.</param>
|
||||
/// <param name="requestBody">Request body.</param>
|
||||
protected abstract void OnDispatch(int methodId, object remotedObject, IActorRequestMessageBody requestBody);
|
||||
|
||||
/// <summary>
|
||||
/// Internal - used by Service remoting.
|
||||
/// </summary>
|
||||
/// <param name="interfaceName">Interface Name of the remoting Interface.</param>
|
||||
/// <param name="methodName">Method Name of the remoting method.</param>
|
||||
/// <param name="methodId">MethodId of the remoting method.</param>
|
||||
/// <param name="remotingMessageBodyFactory">MessageFactory for the remoting Interface.</param>
|
||||
/// <param name="task">continuation task.</param>
|
||||
/// <returns>
|
||||
/// A <see cref="System.Threading.Tasks.Task">Task</see> that represents outstanding operation.
|
||||
/// </returns>
|
||||
/// <typeparam name="TRetVal">The response type for the remoting method.</typeparam>
|
||||
protected Task<IActorResponseMessageBody> ContinueWithResult<TRetVal>(
|
||||
string interfaceName,
|
||||
string methodName,
|
||||
int methodId,
|
||||
IActorMessageBodyFactory remotingMessageBodyFactory,
|
||||
Task<TRetVal> task)
|
||||
{
|
||||
return task.ContinueWith(
|
||||
t => this.CreateResponseMessageBody(interfaceName, methodName, methodId, remotingMessageBodyFactory, t.GetAwaiter().GetResult()),
|
||||
TaskContinuationOptions.ExecuteSynchronously);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Internal - used by remoting.
|
||||
/// </summary>
|
||||
/// <param name="task">continuation task.</param>
|
||||
/// <returns>
|
||||
/// A <see cref="System.Threading.Tasks.Task">Task</see> that represents outstanding operation.
|
||||
/// </returns>
|
||||
protected Task<object> ContinueWith(Task task)
|
||||
{
|
||||
return task.ContinueWith<object>(
|
||||
t =>
|
||||
{
|
||||
t.GetAwaiter().GetResult();
|
||||
return null;
|
||||
},
|
||||
TaskContinuationOptions.ExecuteSynchronously);
|
||||
}
|
||||
|
||||
/// Internal - used by remoting
|
||||
/// <summary>
|
||||
/// This checks if we are wrapping actor message body or not.
|
||||
/// </summary>
|
||||
/// <param name="requestMessageBody">Actor Request Message Body.</param>
|
||||
/// <returns>true or false.</returns>
|
||||
protected bool CheckIfItsWrappedRequest(IActorRequestMessageBody requestMessageBody)
|
||||
{
|
||||
if (requestMessageBody is WrappedMessage)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates Wrapped Response Object for a method.
|
||||
/// </summary>
|
||||
/// <param name="methodId">MethodId of the remoting method.</param>
|
||||
/// <param name="retVal">Response for a method.</param>
|
||||
/// <returns>Wrapped Ressponse object.</returns>
|
||||
// Generated By Code-gen
|
||||
protected abstract object CreateWrappedResponseBody(
|
||||
int methodId,
|
||||
object retVal);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors.Builder
|
||||
{
|
||||
using System;
|
||||
using Microsoft.Actions.Actors.Client;
|
||||
using Microsoft.Actions.Actors.Communication;
|
||||
using Microsoft.Actions.Actors.Communication.Client;
|
||||
|
||||
internal class ActorProxyGenerator
|
||||
{
|
||||
private readonly IProxyActivator proxyActivator;
|
||||
|
||||
public ActorProxyGenerator(
|
||||
Type proxyInterfaceType,
|
||||
IProxyActivator proxyActivator)
|
||||
{
|
||||
this.proxyActivator = proxyActivator;
|
||||
this.ProxyInterfaceType = proxyInterfaceType;
|
||||
}
|
||||
|
||||
public Type ProxyInterfaceType { get; }
|
||||
|
||||
public ActorProxy CreateActorProxy()
|
||||
{
|
||||
return (ActorProxy)this.proxyActivator.CreateInstance();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors.Builder
|
||||
{
|
||||
using System;
|
||||
|
||||
internal class ActorProxyGeneratorBuildResult : BuildResult
|
||||
{
|
||||
public ActorProxyGeneratorBuildResult(CodeBuilderContext buildContext)
|
||||
: base(buildContext)
|
||||
{
|
||||
}
|
||||
|
||||
public Type ProxyType { get; set; }
|
||||
|
||||
public Type ProxyActivatorType { get; set; }
|
||||
|
||||
public ActorProxyGenerator ProxyGenerator { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,570 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors.Builder
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Actions.Actors.Client;
|
||||
using Microsoft.Actions.Actors.Communication;
|
||||
using Microsoft.Actions.Actors.Description;
|
||||
|
||||
internal class ActorProxyGeneratorBuilder : CodeBuilderModule
|
||||
{
|
||||
private readonly Type proxyBaseType;
|
||||
private readonly MethodInfo createMessage;
|
||||
private readonly MethodInfo invokeAsyncMethodInfo;
|
||||
private readonly MethodInfo invokeMethodInfo;
|
||||
private readonly MethodInfo continueWithResultMethodInfo;
|
||||
private readonly MethodInfo continueWithMethodInfo;
|
||||
private readonly MethodInfo checkIfitsWrapped;
|
||||
|
||||
public ActorProxyGeneratorBuilder(ICodeBuilder codeBuilder)
|
||||
: base(codeBuilder)
|
||||
{
|
||||
this.proxyBaseType = typeof(ActorProxy);
|
||||
|
||||
// TODO Should this search change to BindingFlags.NonPublic
|
||||
this.invokeAsyncMethodInfo = this.proxyBaseType.GetMethod(
|
||||
"InvokeAsync",
|
||||
BindingFlags.Instance | BindingFlags.NonPublic,
|
||||
null,
|
||||
CallingConventions.Any,
|
||||
new[] { typeof(int), typeof(int), typeof(string), typeof(IActorRequestMessageBody), typeof(CancellationToken) },
|
||||
null);
|
||||
|
||||
this.checkIfitsWrapped = this.proxyBaseType.GetMethod(
|
||||
"CheckIfItsWrappedRequest",
|
||||
BindingFlags.Instance | BindingFlags.NonPublic,
|
||||
null,
|
||||
CallingConventions.Any,
|
||||
new[] { typeof(IActorRequestMessageBody) },
|
||||
null);
|
||||
|
||||
this.createMessage = this.proxyBaseType.GetMethod(
|
||||
"CreateRequestMessageBody",
|
||||
BindingFlags.Instance | BindingFlags.NonPublic,
|
||||
null,
|
||||
CallingConventions.Any,
|
||||
new[] { typeof(string), typeof(string), typeof(int), typeof(object) },
|
||||
null);
|
||||
|
||||
this.invokeMethodInfo = this.proxyBaseType.GetMethod(
|
||||
"Invoke",
|
||||
BindingFlags.Instance | BindingFlags.NonPublic,
|
||||
null,
|
||||
CallingConventions.Any,
|
||||
new[] { typeof(int), typeof(int), typeof(IActorRequestMessageBody) },
|
||||
null);
|
||||
|
||||
this.continueWithResultMethodInfo = this.proxyBaseType.GetMethod(
|
||||
"ContinueWithResult",
|
||||
BindingFlags.Instance | BindingFlags.NonPublic,
|
||||
null,
|
||||
CallingConventions.Any,
|
||||
new[] { typeof(int), typeof(int), typeof(Task<IActorResponseMessageBody>) },
|
||||
null);
|
||||
|
||||
this.continueWithMethodInfo = this.proxyBaseType.GetMethod(
|
||||
"ContinueWith",
|
||||
BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
}
|
||||
|
||||
protected Type ProxyBaseType => this.proxyBaseType;
|
||||
|
||||
public ActorProxyGeneratorBuildResult Build(
|
||||
Type proxyInterfaceType,
|
||||
IEnumerable<InterfaceDescription> interfaceDescriptions)
|
||||
{
|
||||
// create the context to build the proxy
|
||||
var context = new CodeBuilderContext(
|
||||
assemblyName: this.CodeBuilder.Names.GetProxyAssemblyName(proxyInterfaceType),
|
||||
assemblyNamespace: this.CodeBuilder.Names.GetProxyAssemblyNamespace(proxyInterfaceType),
|
||||
enableDebugging: CodeBuilderAttribute.IsDebuggingEnabled(proxyInterfaceType));
|
||||
var result = new ActorProxyGeneratorBuildResult(context);
|
||||
|
||||
// ensure that method data types are built for each of the remote interfaces
|
||||
var methodBodyTypesResultsMap = interfaceDescriptions.ToDictionary(
|
||||
d => d,
|
||||
d => this.CodeBuilder.GetOrBuildMethodBodyTypes(d.InterfaceType));
|
||||
|
||||
// build the proxy class that implements all of the interfaces explicitly
|
||||
result.ProxyType = this.BuildProxyType(context, proxyInterfaceType, methodBodyTypesResultsMap);
|
||||
|
||||
// build the activator type to create instances of the proxy
|
||||
result.ProxyActivatorType = this.BuildProxyActivatorType(context, proxyInterfaceType, result.ProxyType);
|
||||
|
||||
// build the proxy generator
|
||||
result.ProxyGenerator = this.CreateProxyGenerator(
|
||||
proxyInterfaceType,
|
||||
methodBodyTypesResultsMap,
|
||||
result.ProxyActivatorType);
|
||||
|
||||
context.Complete();
|
||||
return result;
|
||||
}
|
||||
|
||||
internal static LocalBuilder CreateWrappedRequestBody(
|
||||
MethodDescription methodDescription,
|
||||
MethodBodyTypes methodBodyTypes,
|
||||
ILGenerator ilGen,
|
||||
ParameterInfo[] parameters)
|
||||
{
|
||||
var parameterLength = parameters.Length;
|
||||
if (methodDescription.HasCancellationToken)
|
||||
{
|
||||
// Cancellation token is tracked locally and should not be serialized and sent
|
||||
// as a part of the request body.
|
||||
parameterLength = parameterLength - 1;
|
||||
}
|
||||
|
||||
if (parameterLength == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
LocalBuilder wrappedRequestBody = ilGen.DeclareLocal(methodBodyTypes.RequestBodyType);
|
||||
var requestBodyCtor = methodBodyTypes.RequestBodyType.GetConstructor(Type.EmptyTypes);
|
||||
|
||||
if (requestBodyCtor != null)
|
||||
{
|
||||
ilGen.Emit(OpCodes.Newobj, requestBodyCtor);
|
||||
ilGen.Emit(OpCodes.Stloc, wrappedRequestBody);
|
||||
|
||||
var argsLength = parameters.Length;
|
||||
if (methodDescription.HasCancellationToken)
|
||||
{
|
||||
// Cancellation token is tracked locally and should not be serialized and sent
|
||||
// as a part of the request body.
|
||||
argsLength = argsLength - 1;
|
||||
}
|
||||
|
||||
for (var i = 0; i < argsLength; i++)
|
||||
{
|
||||
ilGen.Emit(OpCodes.Ldloc, wrappedRequestBody);
|
||||
ilGen.Emit(OpCodes.Ldarg, i + 1);
|
||||
ilGen.Emit(OpCodes.Stfld, methodBodyTypes.RequestBodyType.GetField(parameters[i].Name));
|
||||
}
|
||||
}
|
||||
|
||||
return wrappedRequestBody;
|
||||
}
|
||||
|
||||
internal void AddVoidMethodImplementation(
|
||||
ILGenerator ilGen,
|
||||
int interfaceDescriptionId,
|
||||
MethodDescription methodDescription,
|
||||
LocalBuilder wrappedRequestBody,
|
||||
string interfaceName)
|
||||
{
|
||||
var interfaceMethod = methodDescription.MethodInfo;
|
||||
|
||||
var parameters = interfaceMethod.GetParameters();
|
||||
|
||||
LocalBuilder requestBody = null;
|
||||
|
||||
if (parameters.Length > 0)
|
||||
{
|
||||
// create IServiceRemotingRequestMessageBody message
|
||||
requestBody = this.CreateRequestRemotingMessageBody(
|
||||
methodDescription,
|
||||
interfaceName,
|
||||
ilGen,
|
||||
parameters.Length,
|
||||
wrappedRequestBody);
|
||||
|
||||
// Check if requestMessage is not implementing WrappedMessage , then call SetParam
|
||||
this.SetParameterIfNeeded(ilGen, requestBody, parameters.Length, parameters);
|
||||
}
|
||||
|
||||
// call the base Invoke method
|
||||
ilGen.Emit(OpCodes.Ldarg_0); // base
|
||||
ilGen.Emit(OpCodes.Ldc_I4, interfaceDescriptionId); // interfaceId
|
||||
ilGen.Emit(OpCodes.Ldc_I4, methodDescription.Id); // methodId
|
||||
|
||||
if (parameters.Length > 0)
|
||||
{
|
||||
ilGen.Emit(OpCodes.Ldloc, requestBody);
|
||||
}
|
||||
else
|
||||
{
|
||||
ilGen.Emit(OpCodes.Ldnull);
|
||||
}
|
||||
|
||||
ilGen.EmitCall(OpCodes.Call, this.invokeMethodInfo, null);
|
||||
}
|
||||
|
||||
protected void AddInterfaceImplementations(
|
||||
TypeBuilder classBuilder,
|
||||
IDictionary<InterfaceDescription, MethodBodyTypesBuildResult> methodBodyTypesResultsMap)
|
||||
{
|
||||
foreach (var item in methodBodyTypesResultsMap)
|
||||
{
|
||||
var interfaceDescription = item.Key;
|
||||
var methodBodyTypesMap = item.Value.MethodBodyTypesMap;
|
||||
|
||||
foreach (var methodDescription in interfaceDescription.Methods)
|
||||
{
|
||||
var methodBodyTypes = methodBodyTypesMap[methodDescription.Name];
|
||||
|
||||
if (TypeUtility.IsTaskType(methodDescription.ReturnType))
|
||||
{
|
||||
this.AddAsyncMethodImplementation(
|
||||
classBuilder,
|
||||
interfaceDescription.Id,
|
||||
methodDescription,
|
||||
methodBodyTypes,
|
||||
interfaceDescription.InterfaceType.FullName);
|
||||
}
|
||||
else if (TypeUtility.IsVoidType(methodDescription.ReturnType))
|
||||
{
|
||||
this.AddVoidMethodImplementation(
|
||||
classBuilder,
|
||||
interfaceDescription.Id,
|
||||
methodDescription,
|
||||
methodBodyTypes,
|
||||
interfaceDescription.InterfaceType.FullName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected ActorProxyGenerator CreateProxyGenerator(
|
||||
Type proxyInterfaceType,
|
||||
IDictionary<InterfaceDescription, MethodBodyTypesBuildResult> methodBodyTypesResultsMap,
|
||||
Type proxyActivatorType)
|
||||
{
|
||||
return this.CreateProxyGenerator(proxyInterfaceType, proxyActivatorType);
|
||||
}
|
||||
|
||||
protected ActorProxyGenerator CreateProxyGenerator(
|
||||
Type proxyInterfaceType,
|
||||
Type proxyActivatorType)
|
||||
{
|
||||
return new ActorProxyGenerator(
|
||||
proxyInterfaceType,
|
||||
(IProxyActivator)Activator.CreateInstance(proxyActivatorType));
|
||||
}
|
||||
|
||||
private static void AddCreateInstanceMethod(
|
||||
TypeBuilder classBuilder,
|
||||
Type proxyType)
|
||||
{
|
||||
var methodBuilder = CodeBuilderUtils.CreatePublicMethodBuilder(
|
||||
classBuilder,
|
||||
"CreateInstance",
|
||||
typeof(IActorProxy));
|
||||
|
||||
var ilGen = methodBuilder.GetILGenerator();
|
||||
var proxyCtor = proxyType.GetConstructor(Type.EmptyTypes);
|
||||
if (proxyCtor != null)
|
||||
{
|
||||
ilGen.Emit(OpCodes.Newobj, proxyCtor);
|
||||
ilGen.Emit(OpCodes.Ret);
|
||||
}
|
||||
else
|
||||
{
|
||||
ilGen.Emit(OpCodes.Ldnull);
|
||||
ilGen.Emit(OpCodes.Ret);
|
||||
}
|
||||
}
|
||||
|
||||
private Type BuildProxyActivatorType(
|
||||
CodeBuilderContext context,
|
||||
Type proxyInterfaceType,
|
||||
Type proxyType)
|
||||
{
|
||||
var classBuilder = CodeBuilderUtils.CreateClassBuilder(
|
||||
context.ModuleBuilder,
|
||||
ns: context.AssemblyNamespace,
|
||||
className: this.CodeBuilder.Names.GetProxyActivatorClassName(proxyInterfaceType),
|
||||
interfaces: new[] { typeof(IProxyActivator) });
|
||||
|
||||
AddCreateInstanceMethod(classBuilder, proxyType);
|
||||
return classBuilder.CreateTypeInfo().AsType();
|
||||
}
|
||||
|
||||
private void AddAsyncMethodImplementation(
|
||||
TypeBuilder classBuilder,
|
||||
int interfaceId,
|
||||
MethodDescription methodDescription,
|
||||
MethodBodyTypes methodBodyTypes,
|
||||
string interfaceName)
|
||||
{
|
||||
var interfaceMethod = methodDescription.MethodInfo;
|
||||
var parameters = interfaceMethod.GetParameters();
|
||||
var methodBuilder = CodeBuilderUtils.CreateExplitInterfaceMethodBuilder(
|
||||
classBuilder,
|
||||
interfaceMethod);
|
||||
var ilGen = methodBuilder.GetILGenerator();
|
||||
var parameterLength = parameters.Length;
|
||||
if (methodDescription.HasCancellationToken)
|
||||
{
|
||||
// Cancellation token is tracked locally and should not be serialized and sent
|
||||
// as a part of the request body.
|
||||
parameterLength = parameterLength - 1;
|
||||
}
|
||||
|
||||
LocalBuilder requestMessage = null;
|
||||
if (parameterLength > 0)
|
||||
{
|
||||
// Create Wrapped Message
|
||||
// create requestBody and assign the values to its field from the arguments
|
||||
var wrappedRequestBody = CreateWrappedRequestBody(methodDescription, methodBodyTypes, ilGen, parameters);
|
||||
|
||||
// create IServiceRemotingRequestMessageBody message
|
||||
requestMessage = this.CreateRequestRemotingMessageBody(methodDescription, interfaceName, ilGen, parameterLength, wrappedRequestBody);
|
||||
|
||||
// Check if requestMessage is not implementing WrappedMessage , then call SetParam
|
||||
this.SetParameterIfNeeded(ilGen, requestMessage, parameterLength, parameters);
|
||||
}
|
||||
|
||||
var objectTask = ilGen.DeclareLocal(typeof(Task<IActorResponseMessageBody>));
|
||||
|
||||
// call the base InvokeAsync method
|
||||
ilGen.Emit(OpCodes.Ldarg_0); // base
|
||||
ilGen.Emit(OpCodes.Ldc_I4, interfaceId); // interfaceId
|
||||
ilGen.Emit(OpCodes.Ldc_I4, methodDescription.Id); // methodId
|
||||
ilGen.Emit(OpCodes.Ldstr, methodDescription.Name); // method name
|
||||
|
||||
if (requestMessage != null)
|
||||
{
|
||||
ilGen.Emit(OpCodes.Ldloc, requestMessage);
|
||||
}
|
||||
else
|
||||
{
|
||||
ilGen.Emit(OpCodes.Ldnull);
|
||||
}
|
||||
|
||||
// Cancellation token argument
|
||||
if (methodDescription.HasCancellationToken)
|
||||
{
|
||||
// Last argument should be the cancellation token
|
||||
var cancellationTokenArgIndex = parameters.Length;
|
||||
ilGen.Emit(OpCodes.Ldarg, cancellationTokenArgIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
var cancellationTokenNone = typeof(CancellationToken).GetMethod("get_None");
|
||||
ilGen.EmitCall(OpCodes.Call, cancellationTokenNone, null);
|
||||
}
|
||||
|
||||
ilGen.EmitCall(OpCodes.Call, this.invokeAsyncMethodInfo, null);
|
||||
ilGen.Emit(OpCodes.Stloc, objectTask);
|
||||
|
||||
// call the base method to get the continuation task and
|
||||
// convert the response body to return value when the task is finished
|
||||
if ((TypeUtility.IsTaskType(methodDescription.ReturnType) &&
|
||||
methodDescription.ReturnType.GetTypeInfo().IsGenericType))
|
||||
{
|
||||
var retvalType = methodDescription.ReturnType.GetGenericArguments()[0];
|
||||
|
||||
ilGen.Emit(OpCodes.Ldarg_0); // base pointer
|
||||
ilGen.Emit(OpCodes.Ldc_I4, interfaceId); // interfaceId
|
||||
ilGen.Emit(OpCodes.Ldc_I4, methodDescription.Id); // methodId
|
||||
ilGen.Emit(OpCodes.Ldloc, objectTask); // task<IServiceRemotingResponseMessageBody>
|
||||
ilGen.Emit(OpCodes.Call, this.continueWithResultMethodInfo.MakeGenericMethod(retvalType));
|
||||
ilGen.Emit(OpCodes.Ret); // return base.ContinueWithResult<TResult>(task);
|
||||
}
|
||||
else
|
||||
{
|
||||
ilGen.Emit(OpCodes.Ldarg_0); // base pointer
|
||||
ilGen.Emit(OpCodes.Ldloc, objectTask); // task<object>
|
||||
ilGen.Emit(OpCodes.Call, this.continueWithMethodInfo);
|
||||
ilGen.Emit(OpCodes.Ret); // return base.ContinueWith(task);
|
||||
}
|
||||
}
|
||||
|
||||
private Type BuildProxyType(
|
||||
CodeBuilderContext context,
|
||||
Type proxyInterfaceType,
|
||||
IDictionary<InterfaceDescription, MethodBodyTypesBuildResult> methodBodyTypesResultsMap)
|
||||
{
|
||||
var classBuilder = CodeBuilderUtils.CreateClassBuilder(
|
||||
context.ModuleBuilder,
|
||||
ns: context.AssemblyNamespace,
|
||||
className: this.CodeBuilder.Names.GetProxyClassName(proxyInterfaceType),
|
||||
baseType: this.proxyBaseType,
|
||||
interfaces: methodBodyTypesResultsMap.Select(item => item.Key.InterfaceType).ToArray());
|
||||
|
||||
this.AddGetReturnValueMethod(classBuilder, methodBodyTypesResultsMap);
|
||||
this.AddInterfaceImplementations(classBuilder, methodBodyTypesResultsMap);
|
||||
|
||||
return classBuilder.CreateTypeInfo().AsType();
|
||||
}
|
||||
|
||||
private void AddGetReturnValueMethod(
|
||||
TypeBuilder classBuilder,
|
||||
IDictionary<InterfaceDescription, MethodBodyTypesBuildResult> methodBodyTypesResultsMap)
|
||||
{
|
||||
var methodBuilder = CodeBuilderUtils.CreateProtectedMethodBuilder(
|
||||
classBuilder,
|
||||
"GetReturnValue",
|
||||
typeof(object), // return value from the reponseBody
|
||||
typeof(int), // interfaceId
|
||||
typeof(int), // methodId
|
||||
typeof(object)); // responseBody
|
||||
|
||||
var ilGen = methodBuilder.GetILGenerator();
|
||||
|
||||
foreach (var item in methodBodyTypesResultsMap)
|
||||
{
|
||||
var interfaceDescription = item.Key;
|
||||
var methodBodyTypesMap = item.Value.MethodBodyTypesMap;
|
||||
|
||||
foreach (var methodDescription in interfaceDescription.Methods)
|
||||
{
|
||||
var methodBodyTypes = methodBodyTypesMap[methodDescription.Name];
|
||||
if (methodBodyTypes.ResponseBodyType == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var elseLabel = ilGen.DefineLabel();
|
||||
|
||||
this.AddIfInterfaceIdAndMethodIdReturnRetvalBlock(
|
||||
ilGen,
|
||||
elseLabel,
|
||||
interfaceDescription.Id,
|
||||
methodDescription.Id,
|
||||
methodBodyTypes.ResponseBodyType);
|
||||
|
||||
ilGen.MarkLabel(elseLabel);
|
||||
}
|
||||
}
|
||||
|
||||
// return null; (if method id's and interfaceId do not mGetReturnValueatch)
|
||||
ilGen.Emit(OpCodes.Ldnull);
|
||||
ilGen.Emit(OpCodes.Ret);
|
||||
}
|
||||
|
||||
private void SetParameterIfNeeded(
|
||||
ILGenerator ilGen,
|
||||
LocalBuilder requestMessage,
|
||||
int parameterLength,
|
||||
ParameterInfo[] parameters)
|
||||
{
|
||||
var boolres = ilGen.DeclareLocal(typeof(bool));
|
||||
var boolres2 = ilGen.DeclareLocal(typeof(bool));
|
||||
ilGen.Emit(OpCodes.Ldarg_0); // base
|
||||
ilGen.Emit(OpCodes.Ldloc_1, requestMessage);
|
||||
ilGen.Emit(OpCodes.Call, this.checkIfitsWrapped);
|
||||
ilGen.Emit(OpCodes.Stloc, boolres);
|
||||
ilGen.Emit(OpCodes.Ldloc_2);
|
||||
ilGen.Emit(OpCodes.Ldc_I4_0);
|
||||
ilGen.Emit(OpCodes.Ceq);
|
||||
ilGen.Emit(OpCodes.Stloc, boolres2);
|
||||
ilGen.Emit(OpCodes.Ldloc_3, boolres2);
|
||||
var elseLabel = ilGen.DefineLabel();
|
||||
ilGen.Emit(OpCodes.Brfalse, elseLabel);
|
||||
|
||||
// if false ,Call SetParamater
|
||||
var setMethod = typeof(IActorRequestMessageBody).GetMethod("SetParameter");
|
||||
|
||||
// Add to Dictionary
|
||||
for (var i = 0; i < parameterLength; i++)
|
||||
{
|
||||
ilGen.Emit(OpCodes.Ldloc, requestMessage);
|
||||
ilGen.Emit(OpCodes.Ldc_I4, i);
|
||||
ilGen.Emit(OpCodes.Ldstr, parameters[i].Name);
|
||||
ilGen.Emit(OpCodes.Ldarg, i + 1);
|
||||
if (!parameters[i].ParameterType.IsClass)
|
||||
{
|
||||
ilGen.Emit(OpCodes.Box, parameters[i].ParameterType);
|
||||
}
|
||||
|
||||
ilGen.Emit(OpCodes.Callvirt, setMethod);
|
||||
}
|
||||
|
||||
ilGen.MarkLabel(elseLabel);
|
||||
}
|
||||
|
||||
private LocalBuilder CreateRequestRemotingMessageBody(
|
||||
MethodDescription methodDescription,
|
||||
string interfaceName,
|
||||
ILGenerator ilGen,
|
||||
int parameterLength,
|
||||
LocalBuilder wrappedRequestBody)
|
||||
{
|
||||
LocalBuilder requestMessage;
|
||||
ilGen.Emit(OpCodes.Ldarg_0); // base
|
||||
requestMessage = ilGen.DeclareLocal(typeof(IActorRequestMessageBody));
|
||||
ilGen.Emit(OpCodes.Ldstr, interfaceName);
|
||||
ilGen.Emit(OpCodes.Ldstr, methodDescription.Name);
|
||||
ilGen.Emit(OpCodes.Ldc_I4, parameterLength);
|
||||
ilGen.Emit(OpCodes.Ldloc, wrappedRequestBody);
|
||||
ilGen.EmitCall(OpCodes.Call, this.createMessage, null);
|
||||
ilGen.Emit(OpCodes.Stloc, requestMessage);
|
||||
return requestMessage;
|
||||
}
|
||||
|
||||
private void AddIfInterfaceIdAndMethodIdReturnRetvalBlock(
|
||||
ILGenerator ilGen,
|
||||
Label elseLabel,
|
||||
int interfaceId,
|
||||
int methodId,
|
||||
Type responseBodyType)
|
||||
{
|
||||
// if (interfaceId == <interfaceId>)
|
||||
ilGen.Emit(OpCodes.Ldarg_1);
|
||||
ilGen.Emit(OpCodes.Ldc_I4, interfaceId);
|
||||
ilGen.Emit(OpCodes.Bne_Un, elseLabel);
|
||||
|
||||
// if (methodId == <methodId>)
|
||||
ilGen.Emit(OpCodes.Ldarg_2);
|
||||
ilGen.Emit(OpCodes.Ldc_I4, methodId);
|
||||
ilGen.Emit(OpCodes.Bne_Un, elseLabel);
|
||||
|
||||
var castedResponseBody = ilGen.DeclareLocal(responseBodyType);
|
||||
ilGen.Emit(OpCodes.Ldarg_3); // load responseBody object
|
||||
ilGen.Emit(OpCodes.Castclass, responseBodyType); // cast it to responseBodyType
|
||||
ilGen.Emit(OpCodes.Stloc, castedResponseBody); // store casted result to castedResponseBody local variable
|
||||
|
||||
var fieldInfo = responseBodyType.GetField(this.CodeBuilder.Names.RetVal);
|
||||
ilGen.Emit(OpCodes.Ldloc, castedResponseBody);
|
||||
ilGen.Emit(OpCodes.Ldfld, fieldInfo);
|
||||
if (!fieldInfo.FieldType.GetTypeInfo().IsClass)
|
||||
{
|
||||
ilGen.Emit(OpCodes.Box, fieldInfo.FieldType);
|
||||
}
|
||||
|
||||
ilGen.Emit(OpCodes.Ret);
|
||||
}
|
||||
|
||||
private void AddVoidMethodImplementation(
|
||||
TypeBuilder classBuilder,
|
||||
int interfaceDescriptionId,
|
||||
MethodDescription methodDescription,
|
||||
MethodBodyTypes methodBodyTypes,
|
||||
string interfaceName)
|
||||
{
|
||||
var interfaceMethod = methodDescription.MethodInfo;
|
||||
|
||||
var methodBuilder = CodeBuilderUtils.CreateExplitInterfaceMethodBuilder(
|
||||
classBuilder,
|
||||
interfaceMethod);
|
||||
|
||||
var ilGen = methodBuilder.GetILGenerator();
|
||||
|
||||
// Create Wrapped Request
|
||||
LocalBuilder wrappedRequestBody =
|
||||
CreateWrappedRequestBody(methodDescription, methodBodyTypes, ilGen, methodDescription.MethodInfo.GetParameters());
|
||||
|
||||
this.AddVoidMethodImplementation(
|
||||
ilGen,
|
||||
interfaceDescriptionId,
|
||||
methodDescription,
|
||||
wrappedRequestBody,
|
||||
interfaceName);
|
||||
|
||||
ilGen.Emit(OpCodes.Ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors.Builder
|
||||
{
|
||||
internal class BuildResult
|
||||
{
|
||||
protected BuildResult(CodeBuilderContext buildContext)
|
||||
{
|
||||
this.BuildContext = buildContext;
|
||||
}
|
||||
|
||||
public CodeBuilderContext BuildContext { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors.Builder
|
||||
{
|
||||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
/// <summary>
|
||||
/// The Attribute class to configure dyanamic code generation process for service remoting.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Interface)]
|
||||
public class CodeBuilderAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CodeBuilderAttribute"/> class.
|
||||
/// </summary>
|
||||
public CodeBuilderAttribute()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to enable debugging flag for the attribute to be used by auto code generation.
|
||||
/// </summary>
|
||||
/// <value><see cref="bool"/> to get or set enable debugging flag for the attribute to be used by auto code generation.</value>
|
||||
public bool EnableDebugging { get; set; }
|
||||
|
||||
internal static bool IsDebuggingEnabled(Type type = null)
|
||||
{
|
||||
var enableDebugging = false;
|
||||
var entryAssembly = Assembly.GetEntryAssembly();
|
||||
|
||||
if (entryAssembly != null)
|
||||
{
|
||||
var attribute = entryAssembly.GetCustomAttribute<CodeBuilderAttribute>();
|
||||
enableDebugging = ((attribute != null) && (attribute.EnableDebugging));
|
||||
}
|
||||
|
||||
if (!enableDebugging && (type != null))
|
||||
{
|
||||
var attribute = type.GetTypeInfo().Assembly.GetCustomAttribute<CodeBuilderAttribute>();
|
||||
enableDebugging = ((attribute != null) && (attribute.EnableDebugging));
|
||||
|
||||
if (!enableDebugging)
|
||||
{
|
||||
attribute = type.GetTypeInfo().GetCustomAttribute<CodeBuilderAttribute>(true);
|
||||
enableDebugging = ((attribute != null) && (attribute.EnableDebugging));
|
||||
}
|
||||
}
|
||||
|
||||
return enableDebugging;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors.Builder
|
||||
{
|
||||
using System.Reflection.Emit;
|
||||
|
||||
internal class CodeBuilderContext
|
||||
{
|
||||
private readonly bool enableDebugging;
|
||||
|
||||
public CodeBuilderContext(string assemblyName, string assemblyNamespace, bool enableDebugging = false)
|
||||
{
|
||||
this.AssemblyNamespace = assemblyNamespace;
|
||||
this.enableDebugging = enableDebugging;
|
||||
|
||||
this.AssemblyBuilder = CodeBuilderUtils.CreateAssemblyBuilder(assemblyName, this.enableDebugging);
|
||||
this.ModuleBuilder = CodeBuilderUtils.CreateModuleBuilder(this.AssemblyBuilder, assemblyName, this.enableDebugging);
|
||||
}
|
||||
|
||||
public AssemblyBuilder AssemblyBuilder { get; }
|
||||
|
||||
public ModuleBuilder ModuleBuilder { get; }
|
||||
|
||||
public string AssemblyNamespace { get; }
|
||||
|
||||
public void Complete()
|
||||
{
|
||||
if (this.enableDebugging)
|
||||
{
|
||||
#if !DotNetCoreClr
|
||||
this.assemblyBuilder.Save(this.assemblyBuilder.GetName().Name + ".dll");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors.Builder
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using Microsoft.Actions.Actors.Description;
|
||||
|
||||
internal abstract class CodeBuilderModule
|
||||
{
|
||||
protected CodeBuilderModule(ICodeBuilder codeBuilder)
|
||||
{
|
||||
this.CodeBuilder = codeBuilder;
|
||||
}
|
||||
|
||||
protected ICodeBuilder CodeBuilder { get; }
|
||||
|
||||
protected static IReadOnlyDictionary<int, string> GetMethodNameMap(InterfaceDescription interfaceDescription)
|
||||
{
|
||||
var methodNameMap = interfaceDescription.Methods.ToDictionary(
|
||||
methodDescription => methodDescription.Id,
|
||||
methodDescription => methodDescription.Name);
|
||||
|
||||
return new ReadOnlyDictionary<int, string>(methodNameMap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,237 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors.Builder
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
internal static class CodeBuilderUtils
|
||||
{
|
||||
private static readonly ConstructorInfo DcAttrCtorInfo;
|
||||
private static readonly PropertyInfo DcAttrNamePropInfo;
|
||||
private static readonly PropertyInfo DcAttrNamespacePropInfo;
|
||||
private static readonly ConstructorInfo DmAttrCtorInfo;
|
||||
private static readonly PropertyInfo DmAttrIsRequiredPropInfo;
|
||||
private static readonly object[] EmptyObjectArray = { };
|
||||
|
||||
static CodeBuilderUtils()
|
||||
{
|
||||
var dcAttrType = typeof(DataContractAttribute);
|
||||
DcAttrCtorInfo = dcAttrType.GetConstructor(Type.EmptyTypes);
|
||||
DcAttrNamePropInfo = dcAttrType.GetProperty("Name");
|
||||
DcAttrNamespacePropInfo = dcAttrType.GetProperty("Namespace");
|
||||
|
||||
var dmAttrType = typeof(DataMemberAttribute);
|
||||
DmAttrCtorInfo = dmAttrType.GetConstructor(Type.EmptyTypes);
|
||||
DmAttrIsRequiredPropInfo = dmAttrType.GetProperty("IsRequired");
|
||||
}
|
||||
|
||||
public static AssemblyBuilder CreateAssemblyBuilder(string assemblyName, bool saveOnDisk = false)
|
||||
{
|
||||
return AssemblyBuilder.DefineDynamicAssembly(
|
||||
new AssemblyName(assemblyName),
|
||||
AssemblyBuilderAccess.RunAndCollect);
|
||||
}
|
||||
|
||||
public static ModuleBuilder CreateModuleBuilder(
|
||||
AssemblyBuilder assemblyBuilder,
|
||||
string moduleName,
|
||||
bool saveOnDisk = false)
|
||||
{
|
||||
return assemblyBuilder.DefineDynamicModule(moduleName);
|
||||
}
|
||||
|
||||
public static TypeBuilder CreateClassBuilder(
|
||||
ModuleBuilder moduleBuilder,
|
||||
string ns,
|
||||
string className,
|
||||
Type baseType = null)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(ns))
|
||||
{
|
||||
className = string.Concat(ns, ".", className);
|
||||
}
|
||||
|
||||
if (baseType != null)
|
||||
{
|
||||
return moduleBuilder.DefineType(
|
||||
className,
|
||||
TypeAttributes.Public | TypeAttributes.Class,
|
||||
baseType);
|
||||
}
|
||||
|
||||
return moduleBuilder.DefineType(
|
||||
className,
|
||||
TypeAttributes.Public | TypeAttributes.Class);
|
||||
}
|
||||
|
||||
public static TypeBuilder CreateClassBuilder(
|
||||
ModuleBuilder moduleBuilder,
|
||||
string ns,
|
||||
string className,
|
||||
IEnumerable<Type> interfaces = null)
|
||||
{
|
||||
return CreateClassBuilder(moduleBuilder, ns, className, null, interfaces);
|
||||
}
|
||||
|
||||
public static TypeBuilder CreateClassBuilder(
|
||||
ModuleBuilder moduleBuilder,
|
||||
string ns,
|
||||
string className,
|
||||
Type baseType,
|
||||
IEnumerable<Type> interfaces)
|
||||
{
|
||||
var typeBuilder = CreateClassBuilder(moduleBuilder, ns, className, baseType);
|
||||
if (interfaces != null)
|
||||
{
|
||||
foreach (var interfaceType in interfaces)
|
||||
{
|
||||
typeBuilder.AddInterfaceImplementation(interfaceType);
|
||||
}
|
||||
}
|
||||
|
||||
return typeBuilder;
|
||||
}
|
||||
|
||||
public static FieldBuilder CreateFieldBuilder(TypeBuilder typeBuilder, Type fieldType, string fieldName)
|
||||
{
|
||||
return typeBuilder.DefineField(
|
||||
fieldName,
|
||||
fieldType,
|
||||
FieldAttributes.Public);
|
||||
}
|
||||
|
||||
public static MethodBuilder CreatePublicMethodBuilder(
|
||||
TypeBuilder typeBuilder,
|
||||
string methodName)
|
||||
{
|
||||
return typeBuilder.DefineMethod(
|
||||
methodName,
|
||||
MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual);
|
||||
}
|
||||
|
||||
public static MethodBuilder CreatePublicMethodBuilder(
|
||||
TypeBuilder typeBuilder,
|
||||
string methodName,
|
||||
Type returnType,
|
||||
params Type[] parameterTypes)
|
||||
{
|
||||
return typeBuilder.DefineMethod(
|
||||
methodName,
|
||||
(MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual),
|
||||
returnType,
|
||||
parameterTypes);
|
||||
}
|
||||
|
||||
public static MethodBuilder CreateProtectedMethodBuilder(
|
||||
TypeBuilder typeBuilder,
|
||||
string methodName,
|
||||
Type returnType,
|
||||
params Type[] parameterTypes)
|
||||
{
|
||||
return typeBuilder.DefineMethod(
|
||||
methodName,
|
||||
(MethodAttributes.Family | MethodAttributes.HideBySig | MethodAttributes.Virtual),
|
||||
returnType,
|
||||
parameterTypes);
|
||||
}
|
||||
|
||||
public static MethodBuilder CreateExplitInterfaceMethodBuilder(
|
||||
TypeBuilder typeBuilder,
|
||||
MethodInfo interfaceMethod)
|
||||
{
|
||||
var parameters = interfaceMethod.GetParameters();
|
||||
var parameterTypes = parameters.Select(pi => pi.ParameterType).ToArray();
|
||||
|
||||
var methodAttrs = (MethodAttributes.Private |
|
||||
MethodAttributes.HideBySig |
|
||||
MethodAttributes.NewSlot |
|
||||
MethodAttributes.Virtual |
|
||||
MethodAttributes.Final);
|
||||
|
||||
var methodBuilder = typeBuilder.DefineMethod(
|
||||
string.Concat(interfaceMethod.DeclaringType.Name, ".", interfaceMethod.Name),
|
||||
methodAttrs,
|
||||
interfaceMethod.ReturnType,
|
||||
parameterTypes);
|
||||
|
||||
typeBuilder.DefineMethodOverride(methodBuilder, interfaceMethod);
|
||||
return methodBuilder;
|
||||
}
|
||||
|
||||
#region Data Contract Type Generation Utilities
|
||||
|
||||
public static TypeBuilder CreateDataContractTypeBuilder(
|
||||
ModuleBuilder moduleBuilder,
|
||||
string ns,
|
||||
string typeName,
|
||||
string dcNamespace = null,
|
||||
string dcName = null)
|
||||
{
|
||||
var typeBuilder = CreateClassBuilder(moduleBuilder, ns, typeName, Type.EmptyTypes);
|
||||
AddDataContractAttribute(typeBuilder, dcNamespace, dcName);
|
||||
return typeBuilder;
|
||||
}
|
||||
|
||||
public static void AddDataMemberField(TypeBuilder dcTypeBuilder, Type fieldType, string fieldName)
|
||||
{
|
||||
var fieldBuilder = CreateFieldBuilder(dcTypeBuilder, fieldType, fieldName);
|
||||
fieldBuilder.SetCustomAttribute(CreateCustomDataMemberAttributeBuilder());
|
||||
}
|
||||
|
||||
private static void AddDataContractAttribute(TypeBuilder typeBuilder, string dcNamespace, string dcName)
|
||||
{
|
||||
typeBuilder.SetCustomAttribute(CreateCustomDataContractAttributeBuilder(dcNamespace, dcName));
|
||||
}
|
||||
|
||||
private static CustomAttributeBuilder CreateCustomDataContractAttributeBuilder(string dcNamespace, string dcName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(dcName) && string.IsNullOrEmpty(dcNamespace))
|
||||
{
|
||||
return new CustomAttributeBuilder(DcAttrCtorInfo, EmptyObjectArray);
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(dcName))
|
||||
{
|
||||
return new CustomAttributeBuilder(
|
||||
DcAttrCtorInfo,
|
||||
EmptyObjectArray,
|
||||
new[] { DcAttrNamespacePropInfo },
|
||||
new object[] { dcNamespace });
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(dcNamespace))
|
||||
{
|
||||
return new CustomAttributeBuilder(
|
||||
DcAttrCtorInfo,
|
||||
EmptyObjectArray,
|
||||
new[] { DcAttrNamePropInfo },
|
||||
new object[] { dcName });
|
||||
}
|
||||
|
||||
return new CustomAttributeBuilder(
|
||||
DcAttrCtorInfo,
|
||||
EmptyObjectArray,
|
||||
new[] { DcAttrNamespacePropInfo, DcAttrNamePropInfo },
|
||||
new object[] { dcNamespace, dcName });
|
||||
}
|
||||
|
||||
private static CustomAttributeBuilder CreateCustomDataMemberAttributeBuilder()
|
||||
{
|
||||
return new CustomAttributeBuilder(
|
||||
DmAttrCtorInfo,
|
||||
EmptyObjectArray,
|
||||
new[] { DmAttrIsRequiredPropInfo },
|
||||
new object[] { false });
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors.Builder
|
||||
{
|
||||
using System;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an interface for generating the code to support communication.
|
||||
/// </summary>
|
||||
internal interface ICodeBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the interface for getting the names of the generated code (types, interfaces, methods etc.)
|
||||
/// </summary>
|
||||
ICodeBuilderNames Names { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or builds a type that can send the communication messages to the object implementing the specified interface.
|
||||
/// </summary>
|
||||
/// <param name="interfaceType">Interface for which to generate the method dispatcher.</param>
|
||||
/// <returns>A <see cref="MethodDispatcherBuildResult"/> containing the dispatcher to dispatch the messages destined the specified interfaces.</returns>
|
||||
MethodDispatcherBuildResult GetOrBuilderMethodDispatcher(Type interfaceType);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or builds a communication message body types that can store the method arguments of the specified interface.
|
||||
/// </summary>
|
||||
/// <param name="interfaceType">Interface for which to generate the method body types.</param>
|
||||
/// <returns>A <see cref="MethodBodyTypesBuildResult"/> containing the method body types for each of the methods of the specified interface.</returns>
|
||||
MethodBodyTypesBuildResult GetOrBuildMethodBodyTypes(Type interfaceType);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or builds a factory object that can generate communication proxy for the specified interface.
|
||||
/// </summary>
|
||||
/// <param name="interfaceType">Interface for which to generate the proxy factory object.</param>
|
||||
/// <returns>A <see cref="ActorProxyGeneratorBuildResult"/> containing the generator for communication proxy for the speficifed interface.</returns>
|
||||
ActorProxyGeneratorBuildResult GetOrBuildProxyGenerator(Type interfaceType);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,118 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors.Builder
|
||||
{
|
||||
using System;
|
||||
|
||||
/// <summary>
|
||||
/// Provides the names for the generated types, methods, arguments, members etc.
|
||||
/// </summary>
|
||||
internal interface ICodeBuilderNames
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the name for the interface Id field.
|
||||
/// </summary>
|
||||
string InterfaceId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name for the method Id field.
|
||||
/// </summary>
|
||||
string MethodId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name for the retval field.
|
||||
/// </summary>
|
||||
string RetVal { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name for the request body field.
|
||||
/// </summary>
|
||||
string RequestBody { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the assembly in which to generate the method body types.
|
||||
/// </summary>
|
||||
/// <param name="interfaceType">The name of the remoted interface.</param>
|
||||
/// <returns>The assembly name for the method body types.</returns>
|
||||
string GetMethodBodyTypesAssemblyName(Type interfaceType);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the namespace of the assembly in which to generate the method body types.
|
||||
/// </summary>
|
||||
/// <param name="interfaceType">The name of the remoted interface.</param>
|
||||
/// <returns>The assembly namespace for the method body types.</returns>
|
||||
string GetMethodBodyTypesAssemblyNamespace(Type interfaceType);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the request body type for the specified method.
|
||||
/// </summary>
|
||||
/// <param name="methodName">Name of the method whose parameters needs to be wraped in the body type.</param>
|
||||
/// <returns>The name of the request body type.</returns>
|
||||
string GetRequestBodyTypeName(string methodName);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the response body type for the specified method.
|
||||
/// </summary>
|
||||
/// <param name="methodName">Name of the method whose return value needs to be wraped in the body type.</param>
|
||||
/// <returns>The name of the response body type.</returns>
|
||||
string GetResponseBodyTypeName(string methodName);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the data contract namespace for the generated types.
|
||||
/// </summary>
|
||||
/// <returns>The data contract namespace.</returns>
|
||||
string GetDataContractNamespace();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the assembly in which to generate the method dispatcher type.
|
||||
/// </summary>
|
||||
/// <param name="interfaceType">The remoted interface type.</param>
|
||||
/// <returns>The name of the assembly for method disptacher.</returns>
|
||||
string GetMethodDispatcherAssemblyName(Type interfaceType);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the namespace of the assembly in which to generate the method dispatcher type.
|
||||
/// </summary>
|
||||
/// <param name="interfaceType">The remoted interface type.</param>
|
||||
/// <returns>The namespace of the assembly for method disptacher.</returns>
|
||||
string GetMethodDispatcherAssemblyNamespace(Type interfaceType);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the method dispatcher class for dispatching methods to the implementation of the specified interface.
|
||||
/// </summary>
|
||||
/// <param name="interfaceType">The remoted interface type.</param>
|
||||
/// <returns>The name of the method dispatcher class.</returns>
|
||||
string GetMethodDispatcherClassName(Type interfaceType);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the assembly in which to generate the proxy of the specified remoted interface.
|
||||
/// </summary>
|
||||
/// <param name="interfaceType">The remoted interface type.</param>
|
||||
/// <returns>The name of the assembly for proxy.</returns>
|
||||
string GetProxyAssemblyName(Type interfaceType);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the namespace of the assembly in which to generate the proxy of the specified remoted interface.
|
||||
/// </summary>
|
||||
/// <param name="interfaceType">The remoted interface type.</param>
|
||||
/// <returns>The namespace of the assembly for proxy.</returns>
|
||||
string GetProxyAssemblyNamespace(Type interfaceType);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the proxy class for the specified interface.
|
||||
/// </summary>
|
||||
/// <param name="interfaceType">The remoted interface type.</param>
|
||||
/// <returns>The name of proxy class.</returns>
|
||||
string GetProxyClassName(Type interfaceType);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the proxy factory (or activator) class for the specified interface.
|
||||
/// </summary>
|
||||
/// <param name="interfaceType">The remoted interface type.</param>
|
||||
/// <returns>The name of proxy activator class.</returns>
|
||||
string GetProxyActivatorClassName(Type interfaceType);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors.Builder
|
||||
{
|
||||
using Microsoft.Actions.Actors.Client;
|
||||
|
||||
/// <summary>
|
||||
/// Interface to create <see cref="ActorProxy"/> objects.
|
||||
/// </summary>
|
||||
public interface IProxyActivator
|
||||
{
|
||||
/// <summary>
|
||||
/// Create the instance of the generated proxy type.
|
||||
/// </summary>
|
||||
/// <returns>An instance of the generated proxy as <see cref="IActorProxy"/>type.</returns>
|
||||
IActorProxy CreateInstance();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors.Builder
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
internal class InterfaceDetails
|
||||
{
|
||||
internal InterfaceDetails()
|
||||
{
|
||||
this.Id = 0;
|
||||
this.MethodNames = new Dictionary<string, int>();
|
||||
this.RequestWrappedKnownTypes = new List<Type>();
|
||||
this.ResponseWrappedKnownTypes = new List<Type>();
|
||||
this.ResponseKnownTypes = new List<Type>();
|
||||
this.ServiceInterfaceType = null;
|
||||
this.RequestKnownTypes = new List<Type>();
|
||||
}
|
||||
|
||||
public Type ServiceInterfaceType { get; internal set; }
|
||||
|
||||
public int Id { get; internal set; }
|
||||
|
||||
public List<Type> RequestKnownTypes { get; internal set; }
|
||||
|
||||
public List<Type> ResponseKnownTypes { get; internal set; }
|
||||
|
||||
public IEnumerable<Type> RequestWrappedKnownTypes { get; internal set; }
|
||||
|
||||
public IEnumerable<Type> ResponseWrappedKnownTypes { get; internal set; }
|
||||
|
||||
public Dictionary<string, int> MethodNames { get; internal set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors.Builder
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Microsoft.Actions.Actors.Description;
|
||||
|
||||
internal class InterfaceDetailsStore
|
||||
{
|
||||
private const string TraceType = "InterfaceDetailsStore";
|
||||
|
||||
private readonly ConcurrentDictionary<int, InterfaceDetails> knownTypesMap =
|
||||
new ConcurrentDictionary<int, InterfaceDetails>();
|
||||
|
||||
private readonly ConcurrentDictionary<string, int> interfaceIdMapping =
|
||||
new ConcurrentDictionary<string, int>();
|
||||
|
||||
public bool TryGetKnownTypes(int interfaceId, out InterfaceDetails interfaceDetails)
|
||||
{
|
||||
return this.knownTypesMap.TryGetValue(interfaceId, out interfaceDetails);
|
||||
}
|
||||
|
||||
public bool TryGetKnownTypes(string interfaceName, out InterfaceDetails interfaceDetails)
|
||||
{
|
||||
if (!this.interfaceIdMapping.TryGetValue(interfaceName, out var interfaceId))
|
||||
{
|
||||
// TODO : Add EventSource diagnostics
|
||||
interfaceDetails = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
return this.knownTypesMap.TryGetValue(interfaceId, out interfaceDetails);
|
||||
}
|
||||
|
||||
public void UpdateKnownTypeDetail(InterfaceDescription interfaceDescription, MethodBodyTypesBuildResult methodBodyTypesBuildResult)
|
||||
{
|
||||
var responseKnownTypes = new List<Type>();
|
||||
var requestKnownType = new List<Type>();
|
||||
foreach (var entry in interfaceDescription.Methods)
|
||||
{
|
||||
if (TypeUtility.IsTaskType(entry.ReturnType) && entry.ReturnType.GetTypeInfo().IsGenericType)
|
||||
{
|
||||
var returnType = entry.MethodInfo.ReturnType.GetGenericArguments()[0];
|
||||
if (!responseKnownTypes.Contains(returnType))
|
||||
{
|
||||
responseKnownTypes.Add(returnType);
|
||||
}
|
||||
}
|
||||
|
||||
requestKnownType.AddRange(entry.MethodInfo.GetParameters()
|
||||
.ToList()
|
||||
.Select(p => p.ParameterType)
|
||||
.Except(requestKnownType));
|
||||
}
|
||||
|
||||
var knownType = new InterfaceDetails
|
||||
{
|
||||
Id = interfaceDescription.Id,
|
||||
ServiceInterfaceType = interfaceDescription.InterfaceType,
|
||||
RequestKnownTypes = requestKnownType,
|
||||
ResponseKnownTypes = responseKnownTypes,
|
||||
MethodNames = interfaceDescription.Methods.ToDictionary(item => item.Name, item => item.Id),
|
||||
RequestWrappedKnownTypes = methodBodyTypesBuildResult.GetRequestBodyTypes(),
|
||||
ResponseWrappedKnownTypes = methodBodyTypesBuildResult.GetResponseBodyTypes(),
|
||||
};
|
||||
this.UpdateKnownTypes(interfaceDescription.Id, interfaceDescription.InterfaceType.FullName, knownType);
|
||||
}
|
||||
|
||||
private void UpdateKnownTypes(
|
||||
int interfaceId,
|
||||
string interfaceName,
|
||||
InterfaceDetails knownTypes)
|
||||
{
|
||||
if (this.knownTypesMap.ContainsKey(interfaceId))
|
||||
{
|
||||
// TODO : Add EventSource diagnostics
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.knownTypesMap.TryAdd(interfaceId, knownTypes))
|
||||
{
|
||||
this.interfaceIdMapping.TryAdd(interfaceName, interfaceId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors.Builder
|
||||
{
|
||||
using System;
|
||||
|
||||
internal class MethodBodyTypes
|
||||
{
|
||||
public Type RequestBodyType { get; set; }
|
||||
|
||||
public Type ResponseBodyType { get; set; }
|
||||
|
||||
public bool HasCancellationTokenArgument { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors.Builder
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
internal class MethodBodyTypesBuildResult : BuildResult
|
||||
{
|
||||
public MethodBodyTypesBuildResult(CodeBuilderContext buildContext)
|
||||
: base(buildContext)
|
||||
{
|
||||
}
|
||||
|
||||
// methodName, methodBodyTypes (RequestType, ResponseType) map
|
||||
public IDictionary<string, MethodBodyTypes> MethodBodyTypesMap { get; set; }
|
||||
|
||||
public IEnumerable<Type> GetRequestBodyTypes()
|
||||
{
|
||||
var result = new List<Type>();
|
||||
if (this.MethodBodyTypesMap != null)
|
||||
{
|
||||
foreach (var item in this.MethodBodyTypesMap)
|
||||
{
|
||||
if (item.Value.RequestBodyType != null)
|
||||
{
|
||||
result.Add(item.Value.RequestBodyType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public IEnumerable<Type> GetResponseBodyTypes()
|
||||
{
|
||||
var result = new List<Type>();
|
||||
if (this.MethodBodyTypesMap != null)
|
||||
{
|
||||
foreach (var item in this.MethodBodyTypesMap)
|
||||
{
|
||||
if (item.Value.ResponseBodyType != null)
|
||||
{
|
||||
result.Add(item.Value.ResponseBodyType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors.Builder
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using Microsoft.Actions.Actors.Description;
|
||||
|
||||
internal class MethodBodyTypesBuilder : CodeBuilderModule
|
||||
{
|
||||
public MethodBodyTypesBuilder(ICodeBuilder codeBuilder)
|
||||
: base(codeBuilder)
|
||||
{
|
||||
}
|
||||
|
||||
public MethodBodyTypesBuildResult Build(InterfaceDescription interfaceDescription)
|
||||
{
|
||||
var context = new CodeBuilderContext(
|
||||
assemblyName: this.CodeBuilder.Names.GetMethodBodyTypesAssemblyName(interfaceDescription.InterfaceType),
|
||||
assemblyNamespace: this.CodeBuilder.Names.GetMethodBodyTypesAssemblyNamespace(interfaceDescription.InterfaceType),
|
||||
enableDebugging: CodeBuilderAttribute.IsDebuggingEnabled(interfaceDescription.InterfaceType));
|
||||
|
||||
var result = new MethodBodyTypesBuildResult(context)
|
||||
{
|
||||
MethodBodyTypesMap = new Dictionary<string, MethodBodyTypes>(),
|
||||
};
|
||||
foreach (var method in interfaceDescription.Methods)
|
||||
{
|
||||
result.MethodBodyTypesMap.Add(
|
||||
method.Name,
|
||||
Build(this.CodeBuilder.Names, context, method));
|
||||
}
|
||||
|
||||
context.Complete();
|
||||
return result;
|
||||
}
|
||||
|
||||
private static MethodBodyTypes Build(
|
||||
ICodeBuilderNames codeBuilderNames,
|
||||
CodeBuilderContext context,
|
||||
MethodDescription methodDescription)
|
||||
{
|
||||
var methodDataTypes = new MethodBodyTypes()
|
||||
{
|
||||
RequestBodyType = null,
|
||||
ResponseBodyType = null,
|
||||
HasCancellationTokenArgument = methodDescription.HasCancellationToken,
|
||||
};
|
||||
|
||||
if ((methodDescription.Arguments != null) && (methodDescription.Arguments.Length != 0))
|
||||
{
|
||||
methodDataTypes.RequestBodyType = BuildRequestBodyType(codeBuilderNames, context, methodDescription);
|
||||
}
|
||||
|
||||
if (TypeUtility.IsTaskType(methodDescription.ReturnType) && methodDescription.ReturnType.GetTypeInfo().IsGenericType)
|
||||
{
|
||||
methodDataTypes.ResponseBodyType = BuildResponseBodyType(codeBuilderNames, context, methodDescription);
|
||||
}
|
||||
|
||||
return methodDataTypes;
|
||||
}
|
||||
|
||||
private static Type BuildRequestBodyType(
|
||||
ICodeBuilderNames codeBuilderNames,
|
||||
CodeBuilderContext context,
|
||||
MethodDescription methodDescription)
|
||||
{
|
||||
var requestBodyTypeBuilder = CodeBuilderUtils.CreateDataContractTypeBuilder(
|
||||
moduleBuilder: context.ModuleBuilder,
|
||||
ns: context.AssemblyNamespace,
|
||||
typeName: codeBuilderNames.GetRequestBodyTypeName(methodDescription.Name),
|
||||
dcNamespace: codeBuilderNames.GetDataContractNamespace());
|
||||
|
||||
foreach (var argument in methodDescription.Arguments)
|
||||
{
|
||||
CodeBuilderUtils.AddDataMemberField(
|
||||
dcTypeBuilder: requestBodyTypeBuilder,
|
||||
fieldType: argument.ArgumentType,
|
||||
fieldName: argument.Name);
|
||||
}
|
||||
|
||||
return requestBodyTypeBuilder.CreateTypeInfo().AsType();
|
||||
}
|
||||
|
||||
private static Type BuildResponseBodyType(
|
||||
ICodeBuilderNames codeBuilderNames,
|
||||
CodeBuilderContext context,
|
||||
MethodDescription methodDescription)
|
||||
{
|
||||
var responseBodyTypeBuilder = CodeBuilderUtils.CreateDataContractTypeBuilder(
|
||||
moduleBuilder: context.ModuleBuilder,
|
||||
ns: context.AssemblyNamespace,
|
||||
typeName: codeBuilderNames.GetResponseBodyTypeName(methodDescription.Name),
|
||||
dcNamespace: codeBuilderNames.GetDataContractNamespace());
|
||||
|
||||
var returnDataType = methodDescription.ReturnType.GetGenericArguments()[0];
|
||||
CodeBuilderUtils.AddDataMemberField(
|
||||
dcTypeBuilder: responseBodyTypeBuilder,
|
||||
fieldType: returnDataType,
|
||||
fieldName: codeBuilderNames.RetVal);
|
||||
|
||||
return responseBodyTypeBuilder.CreateTypeInfo().AsType();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors.Builder
|
||||
{
|
||||
using System;
|
||||
|
||||
internal class MethodDispatcherBuildResult : BuildResult
|
||||
{
|
||||
public MethodDispatcherBuildResult(CodeBuilderContext buildContext)
|
||||
: base(buildContext)
|
||||
{
|
||||
}
|
||||
|
||||
public Type MethodDispatcherType { get; set; }
|
||||
|
||||
public ActorMethodDispatcherBase MethodDispatcher { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,432 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors.Builder
|
||||
{
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Actions.Actors.Communication;
|
||||
using Microsoft.Actions.Actors.Description;
|
||||
|
||||
internal class MethodDispatcherBuilder<TMethodDispatcher> : CodeBuilderModule
|
||||
where TMethodDispatcher : ActorMethodDispatcherBase
|
||||
{
|
||||
private readonly Type methodDispatcherBaseType;
|
||||
private readonly MethodInfo continueWithResultMethodInfo;
|
||||
private readonly MethodInfo continueWithMethodInfo;
|
||||
private readonly MethodInfo checkIfitsWrapped;
|
||||
|
||||
public MethodDispatcherBuilder(ICodeBuilder codeBuilder)
|
||||
: base(codeBuilder)
|
||||
{
|
||||
this.methodDispatcherBaseType = typeof(TMethodDispatcher);
|
||||
|
||||
this.continueWithResultMethodInfo = this.methodDispatcherBaseType.GetMethod(
|
||||
"ContinueWithResult",
|
||||
BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
|
||||
this.continueWithMethodInfo = this.methodDispatcherBaseType.GetMethod(
|
||||
"ContinueWith",
|
||||
BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
this.checkIfitsWrapped = this.methodDispatcherBaseType.GetMethod(
|
||||
"CheckIfItsWrappedRequest",
|
||||
BindingFlags.Instance | BindingFlags.NonPublic,
|
||||
null,
|
||||
CallingConventions.Any,
|
||||
new[] { typeof(IActorRequestMessageBody) },
|
||||
null);
|
||||
}
|
||||
|
||||
public MethodDispatcherBuildResult Build(
|
||||
InterfaceDescription interfaceDescription)
|
||||
{
|
||||
var context = new CodeBuilderContext(
|
||||
assemblyName: this.CodeBuilder.Names.GetMethodDispatcherAssemblyName(interfaceDescription.InterfaceType),
|
||||
assemblyNamespace: this.CodeBuilder.Names.GetMethodDispatcherAssemblyNamespace(interfaceDescription
|
||||
.InterfaceType),
|
||||
enableDebugging: CodeBuilderAttribute.IsDebuggingEnabled(interfaceDescription.InterfaceType));
|
||||
|
||||
var result = new MethodDispatcherBuildResult(context);
|
||||
|
||||
// ensure that the method body types are built
|
||||
var methodBodyTypesBuildResult =
|
||||
this.CodeBuilder.GetOrBuildMethodBodyTypes(interfaceDescription.InterfaceType);
|
||||
|
||||
// build dispatcher class
|
||||
var classBuilder = CodeBuilderUtils.CreateClassBuilder(
|
||||
context.ModuleBuilder,
|
||||
ns: context.AssemblyNamespace,
|
||||
className: this.CodeBuilder.Names.GetMethodDispatcherClassName(interfaceDescription.InterfaceType),
|
||||
baseType: this.methodDispatcherBaseType);
|
||||
|
||||
this.AddCreateResponseBodyMethod(classBuilder, interfaceDescription, methodBodyTypesBuildResult);
|
||||
this.AddOnDispatchAsyncMethod(classBuilder, interfaceDescription, methodBodyTypesBuildResult);
|
||||
this.AddOnDispatchMethod(classBuilder, interfaceDescription, methodBodyTypesBuildResult);
|
||||
|
||||
var methodNameMap = GetMethodNameMap(interfaceDescription);
|
||||
|
||||
// create the dispatcher type, instantiate and initialize it
|
||||
result.MethodDispatcherType = classBuilder.CreateTypeInfo().AsType();
|
||||
result.MethodDispatcher = (TMethodDispatcher)Activator.CreateInstance(result.MethodDispatcherType);
|
||||
var v2MethodDispatcherBase = (ActorMethodDispatcherBase)result.MethodDispatcher;
|
||||
v2MethodDispatcherBase.Initialize(interfaceDescription, methodNameMap);
|
||||
|
||||
context.Complete();
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void AddIfNotWrapMsgGetParameter(
|
||||
ILGenerator ilGen,
|
||||
LocalBuilder castedObject,
|
||||
MethodDescription methodDescription,
|
||||
Type requestBody)
|
||||
{
|
||||
// now invoke the method on the casted object
|
||||
ilGen.Emit(OpCodes.Ldloc, castedObject);
|
||||
|
||||
if ((methodDescription.Arguments != null) && (methodDescription.Arguments.Length != 0))
|
||||
{
|
||||
var method = requestBody.GetMethod("GetParameter");
|
||||
for (var i = 0; i < methodDescription.Arguments.Length; i++)
|
||||
{
|
||||
var argument = methodDescription.Arguments[i];
|
||||
|
||||
// ReSharper disable once AssignNullToNotNullAttribute
|
||||
// castedRequestBody is set to non-null in the previous if check on the same condition
|
||||
ilGen.Emit(OpCodes.Ldarg_3);
|
||||
ilGen.Emit(OpCodes.Ldc_I4, i);
|
||||
ilGen.Emit(OpCodes.Ldstr, argument.Name);
|
||||
ilGen.Emit(OpCodes.Ldtoken, argument.ArgumentType);
|
||||
ilGen.Emit(OpCodes.Callvirt, method);
|
||||
ilGen.Emit(OpCodes.Unbox_Any, argument.ArgumentType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void AddIfWrapMsgGetParameters(
|
||||
ILGenerator ilGen,
|
||||
LocalBuilder castedObject,
|
||||
MethodBodyTypes methodBodyTypes)
|
||||
{
|
||||
LocalBuilder wrappedRequest = null;
|
||||
wrappedRequest = ilGen.DeclareLocal(typeof(object));
|
||||
|
||||
var getValueMethod = typeof(WrappedMessage).GetProperty("Value").GetGetMethod();
|
||||
ilGen.Emit(OpCodes.Ldarg_3); // request object
|
||||
ilGen.Emit(OpCodes.Callvirt, getValueMethod);
|
||||
ilGen.Emit(OpCodes.Stloc, wrappedRequest);
|
||||
|
||||
// then cast and call GetField
|
||||
LocalBuilder castedRequestBody = null;
|
||||
|
||||
if (methodBodyTypes.RequestBodyType != null)
|
||||
{
|
||||
// cast the request body
|
||||
// var castedRequestBody = (<RequestBodyType>)requestBody;
|
||||
castedRequestBody = ilGen.DeclareLocal(methodBodyTypes.RequestBodyType);
|
||||
ilGen.Emit(OpCodes.Ldloc, wrappedRequest); // wrapped request
|
||||
ilGen.Emit(OpCodes.Castclass, methodBodyTypes.RequestBodyType);
|
||||
ilGen.Emit(OpCodes.Stloc, castedRequestBody);
|
||||
}
|
||||
|
||||
// now invoke the method on the casted object
|
||||
ilGen.Emit(OpCodes.Ldloc, castedObject);
|
||||
|
||||
if (methodBodyTypes.RequestBodyType != null)
|
||||
{
|
||||
foreach (var field in methodBodyTypes.RequestBodyType.GetFields())
|
||||
{
|
||||
// ReSharper disable once AssignNullToNotNullAttribute
|
||||
// castedRequestBody is set to non-null in the previous if check on the same condition
|
||||
ilGen.Emit(OpCodes.Ldloc, castedRequestBody);
|
||||
ilGen.Emit(OpCodes.Ldfld, field);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void AddOnDispatchMethod(
|
||||
TypeBuilder classBuilder,
|
||||
InterfaceDescription interfaceDescription,
|
||||
MethodBodyTypesBuildResult methodBodyTypesBuildResult)
|
||||
{
|
||||
var dispatchMethodImpl = CodeBuilderUtils.CreateProtectedMethodBuilder(
|
||||
classBuilder,
|
||||
"OnDispatch",
|
||||
typeof(void),
|
||||
typeof(int), // methodid
|
||||
typeof(object), // remoted object
|
||||
typeof(IActorRequestMessageBody)); // requestBody
|
||||
|
||||
var ilGen = dispatchMethodImpl.GetILGenerator();
|
||||
|
||||
var castedObject = ilGen.DeclareLocal(interfaceDescription.InterfaceType);
|
||||
ilGen.Emit(OpCodes.Ldarg_2); // load remoted object
|
||||
ilGen.Emit(OpCodes.Castclass, interfaceDescription.InterfaceType);
|
||||
ilGen.Emit(OpCodes.Stloc, castedObject); // store casted result to local 0
|
||||
|
||||
foreach (var methodDescription in interfaceDescription.Methods)
|
||||
{
|
||||
if (!TypeUtility.IsVoidType(methodDescription.ReturnType))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var elseLable = ilGen.DefineLabel();
|
||||
|
||||
this.AddIfMethodIdInvokeBlock(
|
||||
ilGen: ilGen,
|
||||
elseLabel: elseLable,
|
||||
castedObject: castedObject,
|
||||
methodDescription: methodDescription,
|
||||
interfaceName: interfaceDescription.InterfaceType.FullName,
|
||||
methodBodyTypes: methodBodyTypesBuildResult.MethodBodyTypesMap[methodDescription.Name]);
|
||||
|
||||
ilGen.MarkLabel(elseLable);
|
||||
}
|
||||
|
||||
ilGen.ThrowException(typeof(MissingMethodException));
|
||||
}
|
||||
|
||||
private void AddIfMethodIdInvokeBlock(
|
||||
ILGenerator ilGen,
|
||||
Label elseLabel,
|
||||
LocalBuilder castedObject,
|
||||
MethodDescription methodDescription,
|
||||
string interfaceName,
|
||||
MethodBodyTypes methodBodyTypes)
|
||||
{
|
||||
ilGen.Emit(OpCodes.Ldarg_1);
|
||||
ilGen.Emit(OpCodes.Ldc_I4, methodDescription.Id);
|
||||
ilGen.Emit(OpCodes.Bne_Un, elseLabel);
|
||||
|
||||
// Check If its Wrapped , then call getparam
|
||||
var requestBody = typeof(IActorRequestMessageBody);
|
||||
|
||||
// now invoke the method on the casted object
|
||||
ilGen.Emit(OpCodes.Ldloc, castedObject);
|
||||
|
||||
// Check if its WrappedMessage
|
||||
var elseLabelforWrapped = ilGen.DefineLabel();
|
||||
this.AddACheckIfItsWrappedMessage(ilGen, elseLabelforWrapped);
|
||||
var endlabel = ilGen.DefineLabel();
|
||||
|
||||
// 2 If true then call GetValue
|
||||
AddIfWrapMsgGetParameters(ilGen, castedObject, methodBodyTypes);
|
||||
ilGen.Emit(OpCodes.Br, endlabel);
|
||||
ilGen.MarkLabel(elseLabelforWrapped);
|
||||
|
||||
// else call GetParameter on IServiceRemotingMessageBody
|
||||
AddIfNotWrapMsgGetParameter(ilGen, castedObject, methodDescription, requestBody);
|
||||
|
||||
ilGen.MarkLabel(endlabel);
|
||||
|
||||
ilGen.EmitCall(OpCodes.Callvirt, methodDescription.MethodInfo, null);
|
||||
ilGen.Emit(OpCodes.Ret);
|
||||
}
|
||||
|
||||
private void AddOnDispatchAsyncMethod(
|
||||
TypeBuilder classBuilder,
|
||||
InterfaceDescription interfaceDescription,
|
||||
MethodBodyTypesBuildResult methodBodyTypesBuildResult)
|
||||
{
|
||||
var dispatchMethodImpl = CodeBuilderUtils.CreateProtectedMethodBuilder(
|
||||
classBuilder,
|
||||
"OnDispatchAsync",
|
||||
typeof(Task<IActorResponseMessageBody>),
|
||||
typeof(int), // methodid
|
||||
typeof(object), // remoted object
|
||||
typeof(IActorRequestMessageBody), // requestBody
|
||||
typeof(IActorMessageBodyFactory), // remotingmessageBodyFactory
|
||||
typeof(CancellationToken)); // CancellationToken
|
||||
|
||||
var ilGen = dispatchMethodImpl.GetILGenerator();
|
||||
|
||||
var castedObject = ilGen.DeclareLocal(interfaceDescription.InterfaceType);
|
||||
ilGen.Emit(OpCodes.Ldarg_2); // load remoted object
|
||||
ilGen.Emit(OpCodes.Castclass, interfaceDescription.InterfaceType);
|
||||
ilGen.Emit(OpCodes.Stloc, castedObject); // store casted result to local 0
|
||||
|
||||
foreach (var methodDescription in interfaceDescription.Methods)
|
||||
{
|
||||
if (!TypeUtility.IsTaskType(methodDescription.ReturnType))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var elseLable = ilGen.DefineLabel();
|
||||
|
||||
this.AddIfMethodIdInvokeAsyncBlock(
|
||||
ilGen: ilGen,
|
||||
elseLabel: elseLable,
|
||||
castedObject: castedObject,
|
||||
methodDescription: methodDescription,
|
||||
interfaceName: interfaceDescription.InterfaceType.FullName,
|
||||
methodBodyTypes: methodBodyTypesBuildResult.MethodBodyTypesMap[methodDescription.Name]);
|
||||
|
||||
ilGen.MarkLabel(elseLable);
|
||||
}
|
||||
|
||||
ilGen.ThrowException(typeof(MissingMethodException));
|
||||
}
|
||||
|
||||
private void AddIfMethodIdInvokeAsyncBlock(
|
||||
ILGenerator ilGen,
|
||||
Label elseLabel,
|
||||
LocalBuilder castedObject,
|
||||
MethodDescription methodDescription,
|
||||
string interfaceName,
|
||||
MethodBodyTypes methodBodyTypes)
|
||||
{
|
||||
ilGen.Emit(OpCodes.Ldarg_1);
|
||||
ilGen.Emit(OpCodes.Ldc_I4, methodDescription.Id);
|
||||
ilGen.Emit(OpCodes.Bne_Un, elseLabel);
|
||||
|
||||
var invokeTask = ilGen.DeclareLocal(methodDescription.ReturnType);
|
||||
var requestBody = typeof(IActorRequestMessageBody);
|
||||
|
||||
// now invoke the method on the casted object
|
||||
ilGen.Emit(OpCodes.Ldloc, castedObject);
|
||||
|
||||
// Check if its WrappedMessage
|
||||
var elseLabelforWrapped = ilGen.DefineLabel();
|
||||
this.AddACheckIfItsWrappedMessage(ilGen, elseLabelforWrapped);
|
||||
var endlabel = ilGen.DefineLabel();
|
||||
|
||||
// 2 If true then call GetValue
|
||||
AddIfWrapMsgGetParameters(ilGen, castedObject, methodBodyTypes);
|
||||
ilGen.Emit(OpCodes.Br, endlabel);
|
||||
ilGen.MarkLabel(elseLabelforWrapped);
|
||||
|
||||
// else call GetParameter on IServiceRemotingMessageBody
|
||||
AddIfNotWrapMsgGetParameter(ilGen, castedObject, methodDescription, requestBody);
|
||||
|
||||
ilGen.MarkLabel(endlabel);
|
||||
|
||||
if (methodDescription.HasCancellationToken)
|
||||
{
|
||||
ilGen.Emit(OpCodes.Ldarg, 5);
|
||||
}
|
||||
|
||||
ilGen.EmitCall(OpCodes.Callvirt, methodDescription.MethodInfo, null);
|
||||
ilGen.Emit(OpCodes.Stloc, invokeTask);
|
||||
|
||||
// call the base method to return continuation task
|
||||
if (TypeUtility.IsTaskType(methodDescription.ReturnType) &&
|
||||
methodDescription.ReturnType.GetTypeInfo().IsGenericType)
|
||||
{
|
||||
// the return is Task<IServiceRemotingMessageBody>
|
||||
var continueWithGenericMethodInfo =
|
||||
this.continueWithResultMethodInfo.MakeGenericMethod(methodDescription.ReturnType
|
||||
.GenericTypeArguments[0]);
|
||||
|
||||
ilGen.Emit(OpCodes.Ldarg_0); // base
|
||||
ilGen.Emit(OpCodes.Ldstr, interfaceName);
|
||||
ilGen.Emit(OpCodes.Ldstr, methodDescription.Name);
|
||||
ilGen.Emit(OpCodes.Ldc_I4, methodDescription.Id);
|
||||
ilGen.Emit(OpCodes.Ldarg, 4); // message body factory
|
||||
ilGen.Emit(OpCodes.Ldloc, invokeTask);
|
||||
ilGen.EmitCall(OpCodes.Call, continueWithGenericMethodInfo, null);
|
||||
ilGen.Emit(OpCodes.Ret);
|
||||
}
|
||||
else
|
||||
{
|
||||
ilGen.Emit(OpCodes.Ldarg_0);
|
||||
ilGen.Emit(OpCodes.Ldloc, invokeTask);
|
||||
ilGen.EmitCall(OpCodes.Call, this.continueWithMethodInfo, null);
|
||||
ilGen.Emit(OpCodes.Ret);
|
||||
}
|
||||
}
|
||||
|
||||
private void AddACheckIfItsWrappedMessage(
|
||||
ILGenerator ilGen, Label elseLabelforWrapped)
|
||||
{
|
||||
var boolres = ilGen.DeclareLocal(typeof(bool));
|
||||
ilGen.Emit(OpCodes.Ldarg_3); // request object
|
||||
ilGen.Emit(OpCodes.Call, this.checkIfitsWrapped);
|
||||
ilGen.Emit(OpCodes.Stloc, boolres);
|
||||
ilGen.Emit(OpCodes.Ldloc, boolres);
|
||||
ilGen.Emit(OpCodes.Brfalse, elseLabelforWrapped);
|
||||
}
|
||||
|
||||
private void AddCreateResponseBodyMethod(
|
||||
TypeBuilder classBuilder,
|
||||
InterfaceDescription interfaceDescription,
|
||||
MethodBodyTypesBuildResult methodBodyTypesBuildResult)
|
||||
{
|
||||
var methodBuilder = CodeBuilderUtils.CreateProtectedMethodBuilder(
|
||||
classBuilder,
|
||||
"CreateWrappedResponseBody",
|
||||
typeof(object), // responseBody - return value
|
||||
typeof(int), // methodId
|
||||
typeof(object)); // retval from the invoked method on the remoted object
|
||||
|
||||
var ilGen = methodBuilder.GetILGenerator();
|
||||
|
||||
foreach (var methodDescription in interfaceDescription.Methods)
|
||||
{
|
||||
var methodBodyTypes = methodBodyTypesBuildResult.MethodBodyTypesMap[methodDescription.Name];
|
||||
if (methodBodyTypes.ResponseBodyType == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var elseLabel = ilGen.DefineLabel();
|
||||
|
||||
this.AddIfMethodIdCreateResponseBlock(
|
||||
ilGen,
|
||||
elseLabel,
|
||||
methodDescription.Id,
|
||||
methodBodyTypes.ResponseBodyType);
|
||||
|
||||
ilGen.MarkLabel(elseLabel);
|
||||
}
|
||||
|
||||
// return null; (if method id's do not match)
|
||||
ilGen.Emit(OpCodes.Ldnull);
|
||||
ilGen.Emit(OpCodes.Ret);
|
||||
}
|
||||
|
||||
private void AddIfMethodIdCreateResponseBlock(
|
||||
ILGenerator ilGen,
|
||||
Label elseLabel,
|
||||
int methodId,
|
||||
Type responseType)
|
||||
{
|
||||
// if (methodId == <methodid>)
|
||||
ilGen.Emit(OpCodes.Ldarg_1);
|
||||
ilGen.Emit(OpCodes.Ldc_I4, methodId);
|
||||
ilGen.Emit(OpCodes.Bne_Un, elseLabel);
|
||||
|
||||
var ctorInfo = responseType.GetConstructor(Type.EmptyTypes);
|
||||
if (ctorInfo != null)
|
||||
{
|
||||
var localBuilder = ilGen.DeclareLocal(responseType);
|
||||
|
||||
// new <ResponseBodyType>
|
||||
ilGen.Emit(OpCodes.Newobj, ctorInfo);
|
||||
ilGen.Emit(OpCodes.Stloc, localBuilder);
|
||||
ilGen.Emit(OpCodes.Ldloc, localBuilder);
|
||||
|
||||
// responseBody.retval = (<retvaltype>)retval;
|
||||
var fInfo = responseType.GetField(this.CodeBuilder.Names.RetVal);
|
||||
ilGen.Emit(OpCodes.Ldarg_2);
|
||||
ilGen.Emit(
|
||||
fInfo.FieldType.GetTypeInfo().IsClass ? OpCodes.Castclass : OpCodes.Unbox_Any,
|
||||
fInfo.FieldType);
|
||||
ilGen.Emit(OpCodes.Stfld, fInfo);
|
||||
ilGen.Emit(OpCodes.Ldloc, localBuilder);
|
||||
ilGen.Emit(OpCodes.Ret);
|
||||
}
|
||||
else
|
||||
{
|
||||
ilGen.Emit(OpCodes.Ldnull);
|
||||
ilGen.Emit(OpCodes.Ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,85 +1,290 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors.Client
|
||||
{
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
/// <summary>
|
||||
/// Provides the base implementation for the proxy to the remote actor objects implementing <see cref="IActor"/> interfaces.
|
||||
/// The proxy object can be used used for client-to-actor and actor-to-actor communication.
|
||||
/// </summary>
|
||||
public class ActorProxy
|
||||
{
|
||||
internal static readonly ActorProxyFactory DefaultProxyFactory = new ActorProxyFactory();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ActorProxy"/> class.
|
||||
/// </summary>
|
||||
protected ActorProxy()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a proxy to the actor object that implements an actor interface.
|
||||
/// </summary>
|
||||
/// <typeparam name="TActorInterface">
|
||||
/// The actor interface implemented by the remote actor object.
|
||||
/// The returned proxy object will implement this interface.
|
||||
/// </typeparam>
|
||||
/// <param name="actorId">The actor ID of the proxy actor object. Methods called on this proxy will result in requests
|
||||
/// being sent to the actor with this ID.</param>
|
||||
/// <param name="actorType">
|
||||
/// Type of actor implementation.
|
||||
/// </param>
|
||||
/// <returns>Proxy to the actor object.</returns>
|
||||
public TActorInterface Create<TActorInterface>(ActorId actorId, Type actorType)
|
||||
where TActorInterface : IActor
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a proxy to the actor object that doesnt implement the actor interface.
|
||||
/// </summary>
|
||||
/// <param name="actorId">The actor ID of the proxy actor object. Methods called on this proxy will result in requests
|
||||
/// being sent to the actor with this ID.</param>
|
||||
/// <param name="actorType">
|
||||
/// Type of actor implementation.
|
||||
/// </param>
|
||||
/// <returns>Proxy to the actor object.</returns>
|
||||
public ActorProxy Create(ActorId actorId, Type actorType)
|
||||
{
|
||||
return new ActorProxy();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the specified method for the actor with provided json payload.
|
||||
/// </summary>
|
||||
/// <param name="method">Actor method name.</param>
|
||||
/// <param name="json">Json payload for actor method.</param>
|
||||
/// <returns>Json response form server.</returns>
|
||||
public async Task<string> InvokeAsync(string method, string json)
|
||||
{
|
||||
await Task.CompletedTask;
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the specified method for the actor with provided json payload.
|
||||
/// </summary>
|
||||
/// <param name="method">Actor method name.</param>
|
||||
/// <param name="json">Json payload for actor method.</param>
|
||||
/// <param name="cancellationToken">Cancellation Token.</param>
|
||||
/// <returns>Json response form server.</returns>
|
||||
public async Task<string> InvokeAsync(string method, string json, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
await Task.CompletedTask;
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors.Client
|
||||
{
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Actions.Actors.Communication;
|
||||
using Microsoft.Actions.Actors.Communication.Client;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
/// <summary>
|
||||
/// Provides the base implementation for the proxy to the remote actor objects implementing <see cref="IActor"/> interfaces.
|
||||
/// The proxy object can be used used for client-to-actor and actor-to-actor communication.
|
||||
/// </summary>
|
||||
public class ActorProxy : IActorProxy
|
||||
{
|
||||
internal static readonly ActorProxyFactory DefaultProxyFactory = new ActorProxyFactory();
|
||||
private ActorRemotingClient actorRemotingClient;
|
||||
private ActorNonRemotingClient actorNonRemotingClient;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ActorProxy"/> class.
|
||||
/// This constructor is protected so that it can be used by generated class which derives from ActorProxy when making Remoting calls.
|
||||
/// This constructor is also marked as internal so that it can be called by ActorProxyFactory when making non-remoting calls.
|
||||
/// </summary>
|
||||
protected internal ActorProxy()
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ActorId ActorId { get; private set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string ActorType { get; private set; }
|
||||
|
||||
internal IActorMessageBodyFactory ActorMessageBodyFactory { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a proxy to the actor object that implements an actor interface.
|
||||
/// </summary>
|
||||
/// <typeparam name="TActorInterface">
|
||||
/// The actor interface implemented by the remote actor object.
|
||||
/// The returned proxy object will implement this interface.
|
||||
/// </typeparam>
|
||||
/// <param name="actorId">The actor ID of the proxy actor object. Methods called on this proxy will result in requests
|
||||
/// being sent to the actor with this ID.</param>
|
||||
/// <param name="actorType">
|
||||
/// Type of actor implementation.
|
||||
/// </param>
|
||||
/// <returns>Proxy to the actor object.</returns>
|
||||
public static TActorInterface Create<TActorInterface>(ActorId actorId, string actorType)
|
||||
where TActorInterface : IActor
|
||||
{
|
||||
return DefaultProxyFactory.CreateActorProxy<TActorInterface>(actorId, actorType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an Actor Proxy for making calls without Remoting.
|
||||
/// </summary>
|
||||
/// <param name="actorId">Actor Id.</param>
|
||||
/// <param name="actorType">Type of actor.</param>
|
||||
/// <returns>Actor proxy to interact with remote actor object.</returns>
|
||||
public static ActorProxy Create(ActorId actorId, string actorType)
|
||||
{
|
||||
return DefaultProxyFactory.Create(actorId, actorType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the specified method for the actor with argument. The argument will be serialized as json.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Return type of method.</typeparam>
|
||||
/// <param name="method">Actor method name.</param>
|
||||
/// <param name="data">Object argument for actor method.</param>
|
||||
/// <param name="cancellationToken">Cancellation Token.</param>
|
||||
/// <returns>Response form server.</returns>
|
||||
public async Task<T> InvokeAsync<T>(string method, object data, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
// TODO: Allow users to provide a custom Serializer.
|
||||
var serializer = new JsonSerializer();
|
||||
var jsonPayload = JsonConvert.SerializeObject(data);
|
||||
var response = await this.actorNonRemotingClient.InvokeActorMethodWithoutRemotingAsync(this.ActorType, this.ActorId.ToString(), method, jsonPayload, cancellationToken);
|
||||
|
||||
using (var streamReader = new StreamReader(response))
|
||||
{
|
||||
using (var reader = new JsonTextReader(streamReader))
|
||||
{
|
||||
return serializer.Deserialize<T>(reader);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the specified method for the actor with argument. The argument will be serialized as json.
|
||||
/// </summary>
|
||||
/// <param name="method">Actor method name.</param>
|
||||
/// <param name="data">Object argument for actor method.</param>
|
||||
/// <param name="cancellationToken">Cancellation Token.</param>
|
||||
/// <returns>Response form server.</returns>
|
||||
public Task InvokeAsync(string method, object data, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
var jsonPayload = JsonConvert.SerializeObject(data);
|
||||
return this.actorNonRemotingClient.InvokeActorMethodWithoutRemotingAsync(this.ActorType, this.ActorId.ToString(), method, jsonPayload, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the specified method for the actor with argument.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Return type of method.</typeparam>
|
||||
/// <param name="method">Actor method name.</param>
|
||||
/// <param name="cancellationToken">Cancellation Token.</param>
|
||||
/// <returns>Response form server.</returns>
|
||||
public async Task<T> InvokeAsync<T>(string method, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
var response = await this.actorNonRemotingClient.InvokeActorMethodWithoutRemotingAsync(this.ActorType, this.ActorId.ToString(), method, null, cancellationToken);
|
||||
var serializer = new JsonSerializer();
|
||||
|
||||
using (var streamReader = new StreamReader(response))
|
||||
{
|
||||
using (var reader = new JsonTextReader(streamReader))
|
||||
{
|
||||
return serializer.Deserialize<T>(reader);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the specified method for the actor with argument.
|
||||
/// </summary>
|
||||
/// <param name="method">Actor method name.</param>
|
||||
/// <param name="cancellationToken">Cancellation Token.</param>
|
||||
/// <returns>Response form server.</returns>
|
||||
public Task InvokeAsync(string method, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return this.actorNonRemotingClient.InvokeActorMethodWithoutRemotingAsync(this.ActorType, this.ActorId.ToString(), method, null, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize whencACtorProxy is created for Remoting.
|
||||
/// </summary>
|
||||
internal void Initialize(
|
||||
ActorRemotingClient client,
|
||||
ActorId actorId,
|
||||
string actorType)
|
||||
{
|
||||
this.actorRemotingClient = client;
|
||||
this.ActorId = actorId;
|
||||
this.ActorType = actorType;
|
||||
this.ActorMessageBodyFactory = client.GetRemotingMessageBodyFactory();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize whenc ActorProxy is created for non-Remoting calls.
|
||||
/// </summary>
|
||||
internal void Initialize(
|
||||
ActorNonRemotingClient client,
|
||||
ActorId actorId,
|
||||
string actorType)
|
||||
{
|
||||
this.actorNonRemotingClient = client;
|
||||
this.ActorId = actorId;
|
||||
this.ActorType = actorType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the specified method for the actor with provided request.
|
||||
/// </summary>
|
||||
/// <param name="interfaceId">Interface ID.</param>
|
||||
/// <param name="methodId">Method ID.</param>
|
||||
/// <param name="methodName">Method Name.</param>
|
||||
/// <param name="requestMsgBodyValue">Request Message Body Value.</param>
|
||||
/// <param name="cancellationToken">Cancellation Token.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> representing the result of the asynchronous operation.</returns>
|
||||
protected async Task<IActorResponseMessageBody> InvokeAsync(
|
||||
int interfaceId,
|
||||
int methodId,
|
||||
string methodName,
|
||||
IActorRequestMessageBody requestMsgBodyValue,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var headers = new ActorRequestMessageHeader
|
||||
{
|
||||
ActorId = this.ActorId,
|
||||
ActorType = this.ActorType,
|
||||
InterfaceId = interfaceId,
|
||||
MethodId = methodId,
|
||||
CallContext = Actors.Helper.GetCallContext(),
|
||||
MethodName = methodName,
|
||||
};
|
||||
|
||||
var responseMsg = await this.actorRemotingClient.InvokeAsync(
|
||||
new ActorRequestMessage(
|
||||
headers,
|
||||
requestMsgBodyValue),
|
||||
methodName,
|
||||
cancellationToken);
|
||||
|
||||
return responseMsg?.GetBody();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the Actor request message Body.
|
||||
/// </summary>
|
||||
/// <param name="interfaceName">Full Name of the service interface for which this call is invoked.</param>
|
||||
/// <param name="methodName">Method Name of the service interface for which this call is invoked.</param>
|
||||
/// <param name="parameterCount">Number of Parameters in the service interface Method.</param>
|
||||
/// <param name="wrappedRequest">Wrapped Request Object.</param>
|
||||
/// <returns>A request message body.</returns>
|
||||
protected IActorRequestMessageBody CreateRequestMessageBody(
|
||||
string interfaceName,
|
||||
string methodName,
|
||||
int parameterCount,
|
||||
object wrappedRequest)
|
||||
{
|
||||
return this.ActorMessageBodyFactory.CreateRequestMessageBody(interfaceName, methodName, parameterCount, wrappedRequest);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method is used by the generated proxy type and should be used directly. This method converts the Task with object
|
||||
/// return value to a Task without the return value for the void method invocation.
|
||||
/// </summary>
|
||||
/// <param name="task">A task returned from the method that contains null return value.</param>
|
||||
/// <returns>A task that represents the asynchronous operation for remote method call without the return value.</returns>
|
||||
protected Task ContinueWith(Task<object> task)
|
||||
{
|
||||
return task;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method is used by the generated proxy type and should be used directly. This method converts the Task with object
|
||||
/// return value to a Task without the return value for the void method invocation.
|
||||
/// </summary>
|
||||
/// <param name="interfaceId">Interface Id for the actor interface.</param>
|
||||
/// <param name="methodId">Method Id for the actor method.</param>
|
||||
/// <param name="responseBody">Response body.</param>
|
||||
/// <returns>Return value of method call as <see cref="object"/>.</returns>
|
||||
protected virtual object GetReturnValue(int interfaceId, int methodId, object responseBody)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called by the generated proxy class to get the result from the response body.
|
||||
/// </summary>
|
||||
/// <typeparam name="TRetval"><see cref="System.Type"/> of the remote method return value.</typeparam>
|
||||
/// <param name="interfaceId">InterfaceId of the remoting interface.</param>
|
||||
/// <param name="methodId">MethodId of the remoting Method.</param>
|
||||
/// <param name="task">A task that represents the asynchronous operation for remote method call.</param>
|
||||
/// <returns>A task that represents the asynchronous operation for remote method call.
|
||||
/// The value of the TRetval contains the remote method return value. </returns>
|
||||
protected async Task<TRetval> ContinueWithResult<TRetval>(
|
||||
int interfaceId,
|
||||
int methodId,
|
||||
Task<IActorResponseMessageBody> task)
|
||||
{
|
||||
var responseBody = await task;
|
||||
if (responseBody is WrappedMessage wrappedMessage)
|
||||
{
|
||||
var obj = this.GetReturnValue(
|
||||
interfaceId,
|
||||
methodId,
|
||||
wrappedMessage.Value);
|
||||
|
||||
return (TRetval)obj;
|
||||
}
|
||||
|
||||
return (TRetval)responseBody.Get(typeof(TRetval));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This check if we are wrapping actor message or not.
|
||||
/// </summary>
|
||||
/// <param name="requestMessageBody">Actor Request Message Body.</param>
|
||||
/// <returns>true or false. </returns>
|
||||
protected bool CheckIfItsWrappedRequest(IActorRequestMessageBody requestMessageBody)
|
||||
{
|
||||
if (requestMessageBody is WrappedMessage)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,35 +7,59 @@ namespace Microsoft.Actions.Actors.Client
|
|||
{
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
|
||||
using Microsoft.Actions.Actors.Builder;
|
||||
using Microsoft.Actions.Actors.Communication.Client;
|
||||
using Microsoft.Actions.Actors.Runtime;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a factory class to create a proxy to the remote actor objects.
|
||||
/// </summary>
|
||||
public class ActorProxyFactory : IActorProxyFactory
|
||||
{
|
||||
internal class ActorProxyFactory : IActorProxyFactory
|
||||
{
|
||||
private readonly IActionsInteractor actionsInteractor;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ActorProxyFactory"/> class.
|
||||
/// TODO: Accept Retry settings.
|
||||
/// </summary>
|
||||
public ActorProxyFactory()
|
||||
{
|
||||
// TODO: Configure HttpClient properties.
|
||||
this.HttpClient = new HttpClient();
|
||||
// TODO: Allow configuration of serialization and client settings.
|
||||
this.actionsInteractor = new ActionsHttpInteractor();
|
||||
}
|
||||
|
||||
internal HttpClient HttpClient { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public TActorInterface CreateActorProxy<TActorInterface>(ActorId actorId, Type actorType)
|
||||
public TActorInterface CreateActorProxy<TActorInterface>(ActorId actorId, string actorType)
|
||||
where TActorInterface : IActor
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ActorProxy CreateActorProxy(ActorId actorId, Type actorType)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
{
|
||||
return (TActorInterface)this.CreateActorProxy(actorId, typeof(TActorInterface), actorType);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ActorProxy Create(ActorId actorId, string actorType)
|
||||
{
|
||||
var actorProxy = new ActorProxy();
|
||||
var nonRemotingClient = new ActorNonRemotingClient(this.actionsInteractor);
|
||||
actorProxy.Initialize(nonRemotingClient, actorId, actorType);
|
||||
|
||||
return actorProxy;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a proxy, this method is also sued by ACtorReference also to create proxy.
|
||||
/// </summary>
|
||||
/// <param name="actorId">Actor Id.</param>
|
||||
/// <param name="actorInterfaceType">Actor Interface Type.</param>
|
||||
/// <param name="actorType">Actor implementation Type.</param>
|
||||
/// <returns>Returns Actor Proxy.</returns>
|
||||
internal object CreateActorProxy(ActorId actorId, Type actorInterfaceType, string actorType)
|
||||
{
|
||||
var remotingClient = new ActorRemotingClient(this.actionsInteractor);
|
||||
var proxyGenerator = ActorCodeBuilder.GetOrCreateProxyGenerator(actorInterfaceType);
|
||||
var actorProxy = proxyGenerator.CreateActorProxy();
|
||||
actorProxy.Initialize(remotingClient, actorId, actorType);
|
||||
|
||||
return actorProxy;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors.Client
|
||||
{
|
||||
using Microsoft.Actions.Actors.Communication.Client;
|
||||
|
||||
/// <summary>
|
||||
/// Provides the interface for implementation of proxy access for actor service.
|
||||
/// </summary>
|
||||
public interface IActorProxy
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets <see cref="Actions.Actors.ActorId"/> associated with the proxy object.
|
||||
/// </summary>
|
||||
/// <value><see cref="Actions.Actors.ActorId"/> associated with the proxy object.</value>
|
||||
ActorId ActorId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets actor implementation type of the actor associated with the proxy object.
|
||||
/// </summary>
|
||||
/// <value>Actor implementation type of the actor associated with the proxy object.</value>
|
||||
string ActorType { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -25,18 +25,15 @@ namespace Microsoft.Actions.Actors.Client
|
|||
/// <returns>An actor proxy object that implements IActorProxy and TActorInterface.</returns>
|
||||
TActorInterface CreateActorProxy<TActorInterface>(
|
||||
ActorId actorId,
|
||||
Type actorType)
|
||||
string actorType)
|
||||
where TActorInterface : IActor;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a proxy to the actor object that doesnt implement the actor interface.
|
||||
/// Creates an Actor Proxy for making calls without Remoting.
|
||||
/// </summary>
|
||||
/// <param name="actorId">The actor ID of the proxy actor object. Methods called on this proxy will result in requests
|
||||
/// being sent to the actor with this ID.</param>
|
||||
/// <param name="actorType">
|
||||
/// Type of actor implementation.
|
||||
/// </param>
|
||||
/// <returns>Actor Proxy object.</returns>
|
||||
ActorProxy CreateActorProxy(ActorId actorId, Type actorType);
|
||||
/// <param name="actorId">Actor Id.</param>
|
||||
/// <param name="actorType">Type of actor.</param>
|
||||
/// <returns>Actor proxy to interact with remote actor object.</returns>
|
||||
ActorProxy Create(ActorId actorId, string actorType);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,29 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors
|
||||
{
|
||||
using System;
|
||||
|
||||
/// <summary>
|
||||
/// Represents connection settings for Http/gRPC Client to interact with Actions runtime.
|
||||
/// </summary>
|
||||
internal class ClientSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ClientSettings"/> class.
|
||||
/// </summary>
|
||||
/// <param name="clientTimeout">Timespan to wait before the request times out for the client.</param>
|
||||
public ClientSettings(TimeSpan? clientTimeout = null)
|
||||
{
|
||||
this.ClientTimeout = clientTimeout;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Timespan to wait before the request times out for the client.
|
||||
/// </summary>
|
||||
public TimeSpan? ClientTimeout { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,134 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors.Common
|
||||
{
|
||||
using System.Globalization;
|
||||
|
||||
/// <summary>
|
||||
/// Computes CRC64 for a given byte payload.
|
||||
/// </summary>
|
||||
internal static class CRC64
|
||||
{
|
||||
/// <summary>
|
||||
/// CRC table.
|
||||
/// </summary>
|
||||
private static readonly ulong[] Crc64Table =
|
||||
{
|
||||
0x0000000000000000, 0x42F0E1EBA9EA3693, 0x85E1C3D753D46D26, 0xC711223CFA3E5BB5,
|
||||
0x493366450E42ECDF, 0x0BC387AEA7A8DA4C, 0xCCD2A5925D9681F9, 0x8E224479F47CB76A,
|
||||
0x9266CC8A1C85D9BE, 0xD0962D61B56FEF2D, 0x17870F5D4F51B498, 0x5577EEB6E6BB820B,
|
||||
0xDB55AACF12C73561, 0x99A54B24BB2D03F2, 0x5EB4691841135847, 0x1C4488F3E8F96ED4,
|
||||
0x663D78FF90E185EF, 0x24CD9914390BB37C, 0xE3DCBB28C335E8C9, 0xA12C5AC36ADFDE5A,
|
||||
0x2F0E1EBA9EA36930, 0x6DFEFF5137495FA3, 0xAAEFDD6DCD770416, 0xE81F3C86649D3285,
|
||||
0xF45BB4758C645C51, 0xB6AB559E258E6AC2, 0x71BA77A2DFB03177, 0x334A9649765A07E4,
|
||||
0xBD68D2308226B08E, 0xFF9833DB2BCC861D, 0x388911E7D1F2DDA8, 0x7A79F00C7818EB3B,
|
||||
0xCC7AF1FF21C30BDE, 0x8E8A101488293D4D, 0x499B3228721766F8, 0x0B6BD3C3DBFD506B,
|
||||
0x854997BA2F81E701, 0xC7B97651866BD192, 0x00A8546D7C558A27, 0x4258B586D5BFBCB4,
|
||||
0x5E1C3D753D46D260, 0x1CECDC9E94ACE4F3, 0xDBFDFEA26E92BF46, 0x990D1F49C77889D5,
|
||||
0x172F5B3033043EBF, 0x55DFBADB9AEE082C, 0x92CE98E760D05399, 0xD03E790CC93A650A,
|
||||
0xAA478900B1228E31, 0xE8B768EB18C8B8A2, 0x2FA64AD7E2F6E317, 0x6D56AB3C4B1CD584,
|
||||
0xE374EF45BF6062EE, 0xA1840EAE168A547D, 0x66952C92ECB40FC8, 0x2465CD79455E395B,
|
||||
0x3821458AADA7578F, 0x7AD1A461044D611C, 0xBDC0865DFE733AA9, 0xFF3067B657990C3A,
|
||||
0x711223CFA3E5BB50, 0x33E2C2240A0F8DC3, 0xF4F3E018F031D676, 0xB60301F359DBE0E5,
|
||||
0xDA050215EA6C212F, 0x98F5E3FE438617BC, 0x5FE4C1C2B9B84C09, 0x1D14202910527A9A,
|
||||
0x93366450E42ECDF0, 0xD1C685BB4DC4FB63, 0x16D7A787B7FAA0D6, 0x5427466C1E109645,
|
||||
0x4863CE9FF6E9F891, 0x0A932F745F03CE02, 0xCD820D48A53D95B7, 0x8F72ECA30CD7A324,
|
||||
0x0150A8DAF8AB144E, 0x43A04931514122DD, 0x84B16B0DAB7F7968, 0xC6418AE602954FFB,
|
||||
0xBC387AEA7A8DA4C0, 0xFEC89B01D3679253, 0x39D9B93D2959C9E6, 0x7B2958D680B3FF75,
|
||||
0xF50B1CAF74CF481F, 0xB7FBFD44DD257E8C, 0x70EADF78271B2539, 0x321A3E938EF113AA,
|
||||
0x2E5EB66066087D7E, 0x6CAE578BCFE24BED, 0xABBF75B735DC1058, 0xE94F945C9C3626CB,
|
||||
0x676DD025684A91A1, 0x259D31CEC1A0A732, 0xE28C13F23B9EFC87, 0xA07CF2199274CA14,
|
||||
0x167FF3EACBAF2AF1, 0x548F120162451C62, 0x939E303D987B47D7, 0xD16ED1D631917144,
|
||||
0x5F4C95AFC5EDC62E, 0x1DBC74446C07F0BD, 0xDAAD56789639AB08, 0x985DB7933FD39D9B,
|
||||
0x84193F60D72AF34F, 0xC6E9DE8B7EC0C5DC, 0x01F8FCB784FE9E69, 0x43081D5C2D14A8FA,
|
||||
0xCD2A5925D9681F90, 0x8FDAB8CE70822903, 0x48CB9AF28ABC72B6, 0x0A3B7B1923564425,
|
||||
0x70428B155B4EAF1E, 0x32B26AFEF2A4998D, 0xF5A348C2089AC238, 0xB753A929A170F4AB,
|
||||
0x3971ED50550C43C1, 0x7B810CBBFCE67552, 0xBC902E8706D82EE7, 0xFE60CF6CAF321874,
|
||||
0xE224479F47CB76A0, 0xA0D4A674EE214033, 0x67C58448141F1B86, 0x253565A3BDF52D15,
|
||||
0xAB1721DA49899A7F, 0xE9E7C031E063ACEC, 0x2EF6E20D1A5DF759, 0x6C0603E6B3B7C1CA,
|
||||
0xF6FAE5C07D3274CD, 0xB40A042BD4D8425E, 0x731B26172EE619EB, 0x31EBC7FC870C2F78,
|
||||
0xBFC9838573709812, 0xFD39626EDA9AAE81, 0x3A28405220A4F534, 0x78D8A1B9894EC3A7,
|
||||
0x649C294A61B7AD73, 0x266CC8A1C85D9BE0, 0xE17DEA9D3263C055, 0xA38D0B769B89F6C6,
|
||||
0x2DAF4F0F6FF541AC, 0x6F5FAEE4C61F773F, 0xA84E8CD83C212C8A, 0xEABE6D3395CB1A19,
|
||||
0x90C79D3FEDD3F122, 0xD2377CD44439C7B1, 0x15265EE8BE079C04, 0x57D6BF0317EDAA97,
|
||||
0xD9F4FB7AE3911DFD, 0x9B041A914A7B2B6E, 0x5C1538ADB04570DB, 0x1EE5D94619AF4648,
|
||||
0x02A151B5F156289C, 0x4051B05E58BC1E0F, 0x87409262A28245BA, 0xC5B073890B687329,
|
||||
0x4B9237F0FF14C443, 0x0962D61B56FEF2D0, 0xCE73F427ACC0A965, 0x8C8315CC052A9FF6,
|
||||
0x3A80143F5CF17F13, 0x7870F5D4F51B4980, 0xBF61D7E80F251235, 0xFD913603A6CF24A6,
|
||||
0x73B3727A52B393CC, 0x31439391FB59A55F, 0xF652B1AD0167FEEA, 0xB4A25046A88DC879,
|
||||
0xA8E6D8B54074A6AD, 0xEA16395EE99E903E, 0x2D071B6213A0CB8B, 0x6FF7FA89BA4AFD18,
|
||||
0xE1D5BEF04E364A72, 0xA3255F1BE7DC7CE1, 0x64347D271DE22754, 0x26C49CCCB40811C7,
|
||||
0x5CBD6CC0CC10FAFC, 0x1E4D8D2B65FACC6F, 0xD95CAF179FC497DA, 0x9BAC4EFC362EA149,
|
||||
0x158E0A85C2521623, 0x577EEB6E6BB820B0, 0x906FC95291867B05, 0xD29F28B9386C4D96,
|
||||
0xCEDBA04AD0952342, 0x8C2B41A1797F15D1, 0x4B3A639D83414E64, 0x09CA82762AAB78F7,
|
||||
0x87E8C60FDED7CF9D, 0xC51827E4773DF90E, 0x020905D88D03A2BB, 0x40F9E43324E99428,
|
||||
0x2CFFE7D5975E55E2, 0x6E0F063E3EB46371, 0xA91E2402C48A38C4, 0xEBEEC5E96D600E57,
|
||||
0x65CC8190991CB93D, 0x273C607B30F68FAE, 0xE02D4247CAC8D41B, 0xA2DDA3AC6322E288,
|
||||
0xBE992B5F8BDB8C5C, 0xFC69CAB42231BACF, 0x3B78E888D80FE17A, 0x7988096371E5D7E9,
|
||||
0xF7AA4D1A85996083, 0xB55AACF12C735610, 0x724B8ECDD64D0DA5, 0x30BB6F267FA73B36,
|
||||
0x4AC29F2A07BFD00D, 0x08327EC1AE55E69E, 0xCF235CFD546BBD2B, 0x8DD3BD16FD818BB8,
|
||||
0x03F1F96F09FD3CD2, 0x41011884A0170A41, 0x86103AB85A2951F4, 0xC4E0DB53F3C36767,
|
||||
0xD8A453A01B3A09B3, 0x9A54B24BB2D03F20, 0x5D45907748EE6495, 0x1FB5719CE1045206,
|
||||
0x919735E51578E56C, 0xD367D40EBC92D3FF, 0x1476F63246AC884A, 0x568617D9EF46BED9,
|
||||
0xE085162AB69D5E3C, 0xA275F7C11F7768AF, 0x6564D5FDE549331A, 0x279434164CA30589,
|
||||
0xA9B6706FB8DFB2E3, 0xEB46918411358470, 0x2C57B3B8EB0BDFC5, 0x6EA7525342E1E956,
|
||||
0x72E3DAA0AA188782, 0x30133B4B03F2B111, 0xF7021977F9CCEAA4, 0xB5F2F89C5026DC37,
|
||||
0x3BD0BCE5A45A6B5D, 0x79205D0E0DB05DCE, 0xBE317F32F78E067B, 0xFCC19ED95E6430E8,
|
||||
0x86B86ED5267CDBD3, 0xC4488F3E8F96ED40, 0x0359AD0275A8B6F5, 0x41A94CE9DC428066,
|
||||
0xCF8B0890283E370C, 0x8D7BE97B81D4019F, 0x4A6ACB477BEA5A2A, 0x089A2AACD2006CB9,
|
||||
0x14DEA25F3AF9026D, 0x562E43B4931334FE, 0x913F6188692D6F4B, 0xD3CF8063C0C759D8,
|
||||
0x5DEDC41A34BBEEB2, 0x1F1D25F19D51D821, 0xD80C07CD676F8394, 0x9AFCE626CE85B507,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Returns the CRC64 for the given payload.
|
||||
/// </summary>
|
||||
/// <param name="value">Byte payload.</param>
|
||||
/// <returns>CRC64 value.</returns>
|
||||
public static ulong ToCRC64(byte[] value)
|
||||
{
|
||||
var crc = 0xffffffffffffffff;
|
||||
for (var i = 0; i < value.Length; i++)
|
||||
{
|
||||
var tableIndex = (((uint)(crc >> 56)) ^ value[i]) & 0xff;
|
||||
crc = Crc64Table[tableIndex] ^ (crc << 8);
|
||||
}
|
||||
|
||||
return crc ^ 0xffffffffffffffff;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the CRC64 for the given payload.
|
||||
/// </summary>
|
||||
/// <param name="values">Byte payloads.</param>
|
||||
/// <returns>CRC64 value.</returns>
|
||||
public static ulong ToCRC64(params byte[][] values)
|
||||
{
|
||||
var crc = 0xffffffffffffffff;
|
||||
for (var x = 0; x < values.Length; x++)
|
||||
{
|
||||
for (var i = 0; i < values[x].Length; i++)
|
||||
{
|
||||
var tableIndex = (((uint)(crc >> 56)) ^ values[x][i]) & 0xff;
|
||||
crc = Crc64Table[tableIndex] ^ (crc << 8);
|
||||
}
|
||||
}
|
||||
|
||||
return crc ^ 0xffffffffffffffff;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the CRC64 in string form for the given payload.
|
||||
/// </summary>
|
||||
/// <param name="value">Byte payload.</param>
|
||||
/// <returns>CRC64 value.</returns>
|
||||
public static string ToCrc64String(byte[] value)
|
||||
{
|
||||
var crc64 = ToCRC64(value);
|
||||
return crc64.ToString("X", CultureInfo.InvariantCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors.Common
|
||||
{
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
|
||||
internal static class IdUtil
|
||||
{
|
||||
internal static int ComputeId(MethodInfo methodInfo)
|
||||
{
|
||||
var hash = methodInfo.Name.GetHashCode();
|
||||
|
||||
if (methodInfo.DeclaringType != null)
|
||||
{
|
||||
if (methodInfo.DeclaringType.Namespace != null)
|
||||
{
|
||||
hash = HashCombine(methodInfo.DeclaringType.Namespace.GetHashCode(), hash);
|
||||
}
|
||||
|
||||
hash = HashCombine(methodInfo.DeclaringType.Name.GetHashCode(), hash);
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
internal static int ComputeId(Type type)
|
||||
{
|
||||
var hash = type.Name.GetHashCode();
|
||||
if (type.Namespace != null)
|
||||
{
|
||||
hash = HashCombine(type.Namespace.GetHashCode(), hash);
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
internal static int ComputeIdWithCRC(Type type)
|
||||
{
|
||||
var name = type.Name;
|
||||
|
||||
if (type.Namespace != null)
|
||||
{
|
||||
name = string.Concat(type.Namespace, name);
|
||||
}
|
||||
|
||||
return ComputeIdWithCRC(name);
|
||||
}
|
||||
|
||||
internal static int ComputeIdWithCRC(MethodInfo methodInfo)
|
||||
{
|
||||
var name = methodInfo.Name;
|
||||
|
||||
if (methodInfo.DeclaringType != null)
|
||||
{
|
||||
if (methodInfo.DeclaringType.Namespace != null)
|
||||
{
|
||||
name = string.Concat(methodInfo.DeclaringType.Namespace, name);
|
||||
}
|
||||
|
||||
name = string.Concat(methodInfo.DeclaringType.Name, name);
|
||||
}
|
||||
|
||||
return ComputeIdWithCRC(name);
|
||||
}
|
||||
|
||||
internal static int ComputeIdWithCRC(string typeName)
|
||||
{
|
||||
return (int)CRC64.ToCRC64(Encoding.UTF8.GetBytes(typeName));
|
||||
}
|
||||
|
||||
internal static int ComputeId(string typeName, string typeNamespace)
|
||||
{
|
||||
var hash = typeName.GetHashCode();
|
||||
if (typeNamespace != null)
|
||||
{
|
||||
hash = HashCombine(typeNamespace.GetHashCode(), hash);
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is how VB Anonymous Types combine hash values for fields.
|
||||
/// </summary>
|
||||
internal static int HashCombine(int newKey, int currentKey)
|
||||
{
|
||||
#pragma warning disable SA1139 // Use literal suffix notation instead of casting
|
||||
return unchecked((currentKey * (int)0xA5555529) + newKey);
|
||||
#pragma warning restore SA1139 // Use literal suffix notation instead of casting
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors.Communication
|
||||
{
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
internal class ActorDataContractSurrogate : ISerializationSurrogateProvider
|
||||
{
|
||||
public static readonly ISerializationSurrogateProvider Instance = new ActorDataContractSurrogate();
|
||||
|
||||
public Type GetSurrogateType(Type type)
|
||||
{
|
||||
if (typeof(IActor).IsAssignableFrom(type))
|
||||
{
|
||||
return typeof(ActorReference);
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
public object GetObjectToSerialize(object obj, Type targetType)
|
||||
{
|
||||
if (obj == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else if (obj is IActor)
|
||||
{
|
||||
return ActorReference.Get(obj);
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
public object GetDeserializedObject(object obj, Type targetType)
|
||||
{
|
||||
if (obj == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else if (obj is IActorReference && typeof(IActor).IsAssignableFrom(targetType) &&
|
||||
!typeof(IActorReference).IsAssignableFrom(targetType))
|
||||
{
|
||||
return ((IActorReference)obj).Bind(targetType);
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors.Communication
|
||||
{
|
||||
using System.Threading;
|
||||
|
||||
internal static class ActorLogicalCallContext
|
||||
{
|
||||
private static AsyncLocal<string> fabActAsyncLocal = new AsyncLocal<string>();
|
||||
|
||||
public static bool IsPresent()
|
||||
{
|
||||
return (fabActAsyncLocal.Value != null);
|
||||
}
|
||||
|
||||
public static bool TryGet(out string callContextValue)
|
||||
{
|
||||
callContextValue = fabActAsyncLocal.Value;
|
||||
return (callContextValue != null);
|
||||
}
|
||||
|
||||
public static void Set(string callContextValue)
|
||||
{
|
||||
fabActAsyncLocal.Value = callContextValue;
|
||||
}
|
||||
|
||||
public static void Clear()
|
||||
{
|
||||
fabActAsyncLocal.Value = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,282 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors.Communication
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Xml;
|
||||
|
||||
/// <summary>
|
||||
/// This is the implmentation for <see cref="IActorMessageBodySerializationProvider"/>used by remoting service and client during
|
||||
/// request/response serialization . It uses request Wrapping and data contract for serialization.
|
||||
/// </summary>
|
||||
internal class ActorMessageBodyDataContractSerializationProvider : IActorMessageBodySerializationProvider
|
||||
{
|
||||
private static readonly IEnumerable<Type> DefaultKnownTypes = new[]
|
||||
{
|
||||
typeof(ActorReference),
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ActorMessageBodyDataContractSerializationProvider"/> class.
|
||||
/// </summary>
|
||||
public ActorMessageBodyDataContractSerializationProvider()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a MessageFactory for Wrapped Message DataContract Remoting Types. This is used to create Remoting Request/Response objects.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// <see cref="IActorMessageBodyFactory" /> that provides an instance of the factory for creating
|
||||
/// remoting request and response message bodies.
|
||||
/// </returns>
|
||||
public IActorMessageBodyFactory CreateMessageBodyFactory()
|
||||
{
|
||||
return new WrappedRequestMessageFactory();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates IActorRequestMessageBodySerializer for a serviceInterface using Wrapped Message DataContract implementation.
|
||||
/// </summary>
|
||||
/// <param name="serviceInterfaceType">The remoted service interface.</param>
|
||||
/// <param name="methodRequestParameterTypes">The union of parameter types of all of the methods of the specified interface.</param>
|
||||
/// <param name="wrappedRequestMessageTypes">Wrapped Request Types for all Methods.</param>
|
||||
/// <returns>
|
||||
/// An instance of the <see cref="IActorRequestMessageBodySerializer" /> that can serialize the service
|
||||
/// actor request message body to a messaging body for transferring over the transport.
|
||||
/// </returns>
|
||||
public IActorRequestMessageBodySerializer CreateRequestMessageBodySerializer(
|
||||
Type serviceInterfaceType,
|
||||
IEnumerable<Type> methodRequestParameterTypes,
|
||||
IEnumerable<Type> wrappedRequestMessageTypes = null)
|
||||
{
|
||||
var knownTypes = new List<Type>(DefaultKnownTypes);
|
||||
knownTypes.AddRange(wrappedRequestMessageTypes);
|
||||
|
||||
DataContractSerializer serializer = this.CreateMessageBodyDataContractSerializer(
|
||||
typeof(WrappedMessageBody),
|
||||
knownTypes);
|
||||
|
||||
return new MemoryStreamMessageBodySerializer<WrappedMessageBody, WrappedMessageBody>(this, serializer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates IActorResponseMessageBodySerializer for a serviceInterface using Wrapped Message DataContract implementation.
|
||||
/// </summary>
|
||||
/// <param name="serviceInterfaceType">The remoted service interface.</param>
|
||||
/// <param name="methodReturnTypes">The return types of all of the methods of the specified interface.</param>
|
||||
/// <param name="wrappedResponseMessageTypes">Wrapped Response Types for all remoting methods.</param>
|
||||
/// <returns>
|
||||
/// An instance of the <see cref="IActorResponseMessageBodySerializer" /> that can serialize the service
|
||||
/// actor response message body to a messaging body for transferring over the transport.
|
||||
/// </returns>
|
||||
public IActorResponseMessageBodySerializer CreateResponseMessageBodySerializer(
|
||||
Type serviceInterfaceType,
|
||||
IEnumerable<Type> methodReturnTypes,
|
||||
IEnumerable<Type> wrappedResponseMessageTypes = null)
|
||||
{
|
||||
var knownTypes = new List<Type>(DefaultKnownTypes);
|
||||
knownTypes.AddRange(wrappedResponseMessageTypes);
|
||||
|
||||
DataContractSerializer serializer = this.CreateMessageBodyDataContractSerializer(
|
||||
typeof(WrappedMessageBody),
|
||||
knownTypes);
|
||||
|
||||
return new MemoryStreamMessageBodySerializer<WrappedMessageBody, WrappedMessageBody>(this, serializer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create the writer to write to the stream. Use this method to customize how the serialized contents are written to
|
||||
/// the stream.
|
||||
/// </summary>
|
||||
/// <param name="outputStream">The stream on which to write the serialized contents.</param>
|
||||
/// <returns>
|
||||
/// An <see cref="System.Xml.XmlDictionaryWriter" /> using which the serializer will write the object on the
|
||||
/// stream.
|
||||
/// </returns>
|
||||
internal XmlDictionaryWriter CreateXmlDictionaryWriter(Stream outputStream)
|
||||
{
|
||||
return XmlDictionaryWriter.CreateBinaryWriter(outputStream);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create the reader to read from the input stream. Use this method to customize how the serialized contents are read
|
||||
/// from the stream.
|
||||
/// </summary>
|
||||
/// <param name="inputStream">The stream from which to read the serialized contents.</param>
|
||||
/// <returns>
|
||||
/// An <see cref="System.Xml.XmlDictionaryReader" /> using which the serializer will read the object from the
|
||||
/// stream.
|
||||
/// </returns>
|
||||
internal XmlDictionaryReader CreateXmlDictionaryReader(Stream inputStream)
|
||||
{
|
||||
return XmlDictionaryReader.CreateBinaryReader(inputStream, XmlDictionaryReaderQuotas.Max);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the settings used to create DataContractSerializer for serializing and de-serializing request message body.
|
||||
/// </summary>
|
||||
/// <param name="remotingRequestType">Remoting RequestMessageBody Type.</param>
|
||||
/// <param name="knownTypes">The return types of all of the methods of the specified interface.</param>
|
||||
/// <returns><see cref="DataContractSerializerSettings" /> for serializing and de-serializing request message body.</returns>
|
||||
internal DataContractSerializer CreateMessageBodyDataContractSerializer(
|
||||
Type remotingRequestType,
|
||||
IEnumerable<Type> knownTypes)
|
||||
{
|
||||
var serializer = new DataContractSerializer(
|
||||
remotingRequestType,
|
||||
new DataContractSerializerSettings()
|
||||
{
|
||||
MaxItemsInObjectGraph = int.MaxValue,
|
||||
KnownTypes = knownTypes,
|
||||
});
|
||||
|
||||
serializer.SetSerializationSurrogateProvider(new ActorDataContractSurrogate());
|
||||
return serializer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default serializer for service remoting request and response message body that uses the
|
||||
/// memory stream to create outgoing message buffers.
|
||||
/// </summary>
|
||||
private class MemoryStreamMessageBodySerializer<TRequest, TResponse> :
|
||||
IActorRequestMessageBodySerializer,
|
||||
IActorResponseMessageBodySerializer
|
||||
where TRequest : IActorRequestMessageBody
|
||||
where TResponse : IActorResponseMessageBody
|
||||
{
|
||||
private readonly ActorMessageBodyDataContractSerializationProvider serializationProvider;
|
||||
private readonly DataContractSerializer serializer;
|
||||
|
||||
public MemoryStreamMessageBodySerializer(
|
||||
ActorMessageBodyDataContractSerializationProvider serializationProvider,
|
||||
DataContractSerializer serializer)
|
||||
{
|
||||
this.serializationProvider = serializationProvider;
|
||||
this.serializer = serializer;
|
||||
}
|
||||
|
||||
byte[] IActorRequestMessageBodySerializer.Serialize(IActorRequestMessageBody actorRequestMessageBody)
|
||||
{
|
||||
if (actorRequestMessageBody == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
using (var stream = new MemoryStream())
|
||||
{
|
||||
using (var writer = this.CreateXmlDictionaryWriter(stream))
|
||||
{
|
||||
this.serializer.WriteObject(writer, actorRequestMessageBody);
|
||||
writer.Flush();
|
||||
|
||||
return stream.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IActorRequestMessageBody IActorRequestMessageBodySerializer.Deserialize(Stream messageBody)
|
||||
{
|
||||
if (messageBody == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// TODO check performance
|
||||
using (var stream = new MemoryStream())
|
||||
{
|
||||
messageBody.CopyTo(stream);
|
||||
stream.Position = 0;
|
||||
|
||||
if (stream.Capacity == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
using (var reader = this.CreateXmlDictionaryReader(stream))
|
||||
{
|
||||
return (TRequest)this.serializer.ReadObject(reader);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
byte[] IActorResponseMessageBodySerializer.Serialize(IActorResponseMessageBody actorResponseMessageBody)
|
||||
{
|
||||
if (actorResponseMessageBody == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
using (var stream = new MemoryStream())
|
||||
{
|
||||
using (var writer = this.CreateXmlDictionaryWriter(stream))
|
||||
{
|
||||
this.serializer.WriteObject(writer, actorResponseMessageBody);
|
||||
writer.Flush();
|
||||
|
||||
return stream.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IActorResponseMessageBody IActorResponseMessageBodySerializer.Deserialize(Stream messageBody)
|
||||
{
|
||||
if (messageBody == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// TODO check performance
|
||||
using (var stream = new MemoryStream())
|
||||
{
|
||||
messageBody.CopyTo(stream);
|
||||
stream.Position = 0;
|
||||
|
||||
if (stream.Capacity == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
using (var reader = this.CreateXmlDictionaryReader(stream))
|
||||
{
|
||||
return (TResponse)this.serializer.ReadObject(reader);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create the writer to write to the stream. Use this method to customize how the serialized contents are written to
|
||||
/// the stream.
|
||||
/// </summary>
|
||||
/// <param name="outputStream">The stream on which to write the serialized contents.</param>
|
||||
/// <returns>
|
||||
/// An <see cref="System.Xml.XmlDictionaryWriter" /> using which the serializer will write the object on the
|
||||
/// stream.
|
||||
/// </returns>
|
||||
private XmlDictionaryWriter CreateXmlDictionaryWriter(Stream outputStream)
|
||||
{
|
||||
return this.serializationProvider.CreateXmlDictionaryWriter(outputStream);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create the reader to read from the input stream. Use this method to customize how the serialized contents are read
|
||||
/// from the stream.
|
||||
/// </summary>
|
||||
/// <param name="inputStream">The stream from which to read the serialized contents.</param>
|
||||
/// <returns>
|
||||
/// An <see cref="System.Xml.XmlDictionaryReader" /> using which the serializer will read the object from the
|
||||
/// stream.
|
||||
/// </returns>
|
||||
private XmlDictionaryReader CreateXmlDictionaryReader(Stream inputStream)
|
||||
{
|
||||
return this.serializationProvider.CreateXmlDictionaryReader(inputStream);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,111 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors.Communication
|
||||
{
|
||||
using System.IO;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Xml;
|
||||
|
||||
internal class ActorMessageHeaderSerializer : IActorMessageHeaderSerializer
|
||||
{
|
||||
private readonly DataContractSerializer requestHeaderSerializer;
|
||||
private readonly DataContractSerializer responseHeaderSerializer;
|
||||
|
||||
public ActorMessageHeaderSerializer()
|
||||
: this(
|
||||
new DataContractSerializer(
|
||||
typeof(IActorRequestMessageHeader),
|
||||
new DataContractSerializerSettings()
|
||||
{
|
||||
MaxItemsInObjectGraph = int.MaxValue,
|
||||
KnownTypes = new[]
|
||||
{
|
||||
typeof(ActorRequestMessageHeader),
|
||||
},
|
||||
}))
|
||||
{
|
||||
}
|
||||
|
||||
public ActorMessageHeaderSerializer(
|
||||
DataContractSerializer headerRequestSerializer)
|
||||
{
|
||||
this.requestHeaderSerializer = headerRequestSerializer;
|
||||
this.responseHeaderSerializer = new DataContractSerializer(
|
||||
typeof(IActorResponseMessageHeader),
|
||||
new DataContractSerializerSettings()
|
||||
{
|
||||
MaxItemsInObjectGraph = int.MaxValue,
|
||||
KnownTypes = new[] { typeof(ActorResponseMessageHeader) },
|
||||
});
|
||||
}
|
||||
|
||||
public byte[] SerializeRequestHeader(IActorRequestMessageHeader serviceRemotingRequestMessageHeader)
|
||||
{
|
||||
if (serviceRemotingRequestMessageHeader == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
using (var stream = new MemoryStream())
|
||||
{
|
||||
using (var writer = XmlDictionaryWriter.CreateTextWriter(stream))
|
||||
{
|
||||
this.requestHeaderSerializer.WriteObject(writer, serviceRemotingRequestMessageHeader);
|
||||
writer.Flush();
|
||||
return stream.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IActorRequestMessageHeader DeserializeRequestHeaders(Stream messageHeader)
|
||||
{
|
||||
if ((messageHeader == null) || (messageHeader.Length == 0))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
using (var reader = XmlDictionaryReader.CreateTextReader(
|
||||
messageHeader,
|
||||
XmlDictionaryReaderQuotas.Max))
|
||||
{
|
||||
return (IActorRequestMessageHeader)this.requestHeaderSerializer.ReadObject(reader);
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] SerializeResponseHeader(IActorResponseMessageHeader serviceRemotingResponseMessageHeader)
|
||||
{
|
||||
if (serviceRemotingResponseMessageHeader == null || serviceRemotingResponseMessageHeader.CheckIfItsEmpty())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
using (var stream = new MemoryStream())
|
||||
{
|
||||
using (var writer = XmlDictionaryWriter.CreateTextWriter(stream))
|
||||
{
|
||||
this.responseHeaderSerializer.WriteObject(writer, serviceRemotingResponseMessageHeader);
|
||||
writer.Flush();
|
||||
return stream.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IActorResponseMessageHeader DeserializeResponseHeaders(Stream messageHeader)
|
||||
{
|
||||
if ((messageHeader == null) || (messageHeader.Length == 0))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
using (var reader = XmlDictionaryReader.CreateTextReader(
|
||||
messageHeader,
|
||||
XmlDictionaryReaderQuotas.Max))
|
||||
{
|
||||
return (IActorResponseMessageHeader)this.responseHeaderSerializer.ReadObject(reader);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors.Communication
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using Microsoft.Actions.Actors.Builder;
|
||||
|
||||
internal class ActorMessageSerializersManager
|
||||
{
|
||||
private readonly ConcurrentDictionary<int, CacheEntry> cachedBodySerializers;
|
||||
private readonly IActorMessageHeaderSerializer headerSerializer;
|
||||
private readonly IActorMessageBodySerializationProvider serializationProvider;
|
||||
|
||||
public ActorMessageSerializersManager(
|
||||
IActorMessageBodySerializationProvider serializationProvider,
|
||||
IActorMessageHeaderSerializer headerSerializer)
|
||||
{
|
||||
if (serializationProvider == null)
|
||||
{
|
||||
serializationProvider = new ActorMessageBodyDataContractSerializationProvider();
|
||||
}
|
||||
|
||||
if (headerSerializer == null)
|
||||
{
|
||||
headerSerializer = new ActorMessageHeaderSerializer();
|
||||
}
|
||||
|
||||
this.serializationProvider = serializationProvider;
|
||||
this.cachedBodySerializers = new ConcurrentDictionary<int, CacheEntry>();
|
||||
this.headerSerializer = headerSerializer;
|
||||
}
|
||||
|
||||
public IActorMessageBodySerializationProvider GetSerializationProvider()
|
||||
{
|
||||
return this.serializationProvider;
|
||||
}
|
||||
|
||||
public IActorMessageHeaderSerializer GetHeaderSerializer()
|
||||
{
|
||||
return this.headerSerializer;
|
||||
}
|
||||
|
||||
public IActorRequestMessageBodySerializer GetRequestMessageBodySerializer(int interfaceId)
|
||||
{
|
||||
return this.cachedBodySerializers.GetOrAdd(interfaceId, this.CreateSerializers).RequestMessageBodySerializer;
|
||||
}
|
||||
|
||||
public IActorResponseMessageBodySerializer GetResponseMessageBodySerializer(int interfaceId)
|
||||
{
|
||||
return this.cachedBodySerializers.GetOrAdd(interfaceId, this.CreateSerializers).ResponseMessageBodySerializer;
|
||||
}
|
||||
|
||||
internal CacheEntry CreateSerializers(int interfaceId)
|
||||
{
|
||||
var interfaceDetails = this.GetInterfaceDetails(interfaceId);
|
||||
|
||||
// get the service interface type from the code gen layer
|
||||
var serviceInterfaceType = interfaceDetails.ServiceInterfaceType;
|
||||
|
||||
// get the known types from the codegen layer
|
||||
var requestBodyTypes = interfaceDetails.RequestKnownTypes;
|
||||
|
||||
// get the known types from the codegen layer
|
||||
var responseBodyTypes = interfaceDetails.ResponseKnownTypes;
|
||||
|
||||
return new CacheEntry(
|
||||
this.serializationProvider.CreateRequestMessageBodySerializer(serviceInterfaceType, requestBodyTypes, interfaceDetails.RequestWrappedKnownTypes),
|
||||
this.serializationProvider.CreateResponseMessageBodySerializer(serviceInterfaceType, responseBodyTypes, interfaceDetails.ResponseWrappedKnownTypes));
|
||||
}
|
||||
|
||||
internal InterfaceDetails GetInterfaceDetails(int interfaceId)
|
||||
{
|
||||
if (!ActorCodeBuilder.TryGetKnownTypes(interfaceId, out var interfaceDetails))
|
||||
{
|
||||
throw new ArgumentException("No interface found with this Id " + interfaceId);
|
||||
}
|
||||
|
||||
return interfaceDetails;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors.Communication
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using Microsoft.Actions.Actors.Builder;
|
||||
using Microsoft.Actions.Actors.Resources;
|
||||
using Microsoft.Actions.Actors.Runtime;
|
||||
|
||||
/// <summary>
|
||||
/// Actor method dispatcher map for remoting calls.
|
||||
/// </summary>
|
||||
internal class ActorMethodDispatcherMap
|
||||
{
|
||||
private readonly IDictionary<int, ActorMethodDispatcherBase> map;
|
||||
|
||||
public ActorMethodDispatcherMap(IEnumerable<Type> interfaceTypes)
|
||||
{
|
||||
this.map = new Dictionary<int, ActorMethodDispatcherBase>();
|
||||
|
||||
foreach (var actorInterfaceType in interfaceTypes)
|
||||
{
|
||||
var methodDispatcher = ActorCodeBuilder.GetOrCreateMethodDispatcher(actorInterfaceType);
|
||||
this.map.Add(methodDispatcher.InterfaceId, methodDispatcher);
|
||||
}
|
||||
}
|
||||
|
||||
public ActorMethodDispatcherBase GetDispatcher(int interfaceId, int methodId)
|
||||
{
|
||||
if (!this.map.TryGetValue(interfaceId, out var methodDispatcher))
|
||||
{
|
||||
throw new KeyNotFoundException(string.Format(
|
||||
CultureInfo.CurrentCulture,
|
||||
SR.ErrorMethodDispatcherNotFound,
|
||||
interfaceId));
|
||||
}
|
||||
|
||||
return methodDispatcher;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors.Communication
|
||||
{
|
||||
internal class ActorRequestMessage : IActorRequestMessage
|
||||
{
|
||||
private readonly IActorRequestMessageHeader header;
|
||||
private readonly IActorRequestMessageBody msgBody;
|
||||
|
||||
public ActorRequestMessage(IActorRequestMessageHeader header, IActorRequestMessageBody msgBody)
|
||||
{
|
||||
this.header = header;
|
||||
this.msgBody = msgBody;
|
||||
}
|
||||
|
||||
public IActorRequestMessageHeader GetHeader()
|
||||
{
|
||||
return this.header;
|
||||
}
|
||||
|
||||
public IActorRequestMessageBody GetBody()
|
||||
{
|
||||
return this.msgBody;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors.Communication
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
[DataContract(Name = "msgBody", Namespace = Constants.Namespace)]
|
||||
internal class ActorRequestMessageBody : IActorRequestMessageBody
|
||||
{
|
||||
[DataMember]
|
||||
private readonly Dictionary<string, object> parameters;
|
||||
|
||||
public ActorRequestMessageBody(int parameterInfos)
|
||||
{
|
||||
this.parameters = new Dictionary<string, object>(parameterInfos);
|
||||
}
|
||||
|
||||
public void SetParameter(int position, string paramName, object parameter)
|
||||
{
|
||||
this.parameters[paramName] = parameter;
|
||||
}
|
||||
|
||||
public object GetParameter(int position, string paramName, Type paramType)
|
||||
{
|
||||
return this.parameters[paramName];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors.Communication
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Runtime.Serialization;
|
||||
using Microsoft.Actions.Actors.Resources;
|
||||
|
||||
[DataContract(Name = "ActorHeader", Namespace = Constants.Namespace)]
|
||||
internal class ActorRequestMessageHeader : IActorRequestMessageHeader
|
||||
{
|
||||
internal const string CancellationHeaderName = "CancellationHeader";
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ActorRequestMessageHeader"/> class.
|
||||
/// </summary>
|
||||
public ActorRequestMessageHeader()
|
||||
{
|
||||
this.headers = new Dictionary<string, byte[]>();
|
||||
this.InvocationId = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the methodId of the remote method.
|
||||
/// </summary>
|
||||
/// <value>Method id.</value>
|
||||
[DataMember(Name = "MethodId", IsRequired = true, Order = 0)]
|
||||
public int MethodId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the interface id of the remote interface.
|
||||
/// </summary>
|
||||
/// <value>Interface id.</value>
|
||||
[DataMember(Name = "InterfaceId", IsRequired = true, Order = 1)]
|
||||
public int InterfaceId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets identifier for the remote method invocation.
|
||||
/// </summary>
|
||||
[DataMember(Name = "InvocationId", IsRequired = false, Order = 2, EmitDefaultValue = false)]
|
||||
public string InvocationId { get; set; }
|
||||
|
||||
[DataMember(IsRequired = false, Order = 3)]
|
||||
public ActorId ActorId { get; set; }
|
||||
|
||||
[DataMember(IsRequired = false, Order = 4)]
|
||||
public string CallContext { get; set; }
|
||||
|
||||
[DataMember(Name = "Headers", IsRequired = true, Order = 5)]
|
||||
|
||||
#pragma warning disable SA1201 // Elements should appear in the correct order. Increases readbility when fields kept in order.
|
||||
private readonly Dictionary<string, byte[]> headers;
|
||||
#pragma warning restore SA1201 // Elements should appear in the correct order
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the method name of the remote method.
|
||||
/// </summary>
|
||||
/// <value>Method Name.</value>
|
||||
[DataMember(Name = "MethodName", IsRequired = false, Order = 6)]
|
||||
public string MethodName { get; set; }
|
||||
|
||||
[DataMember(IsRequired = false, Order = 7)]
|
||||
public string ActorType { get; set; }
|
||||
|
||||
public void AddHeader(string headerName, byte[] headerValue)
|
||||
{
|
||||
if (this.headers.ContainsKey(headerName))
|
||||
{
|
||||
// TODO throw specific translated exception type
|
||||
throw new System.Exception(
|
||||
string.Format(
|
||||
CultureInfo.CurrentCulture,
|
||||
SR.ErrorHeaderAlreadyExists,
|
||||
headerName));
|
||||
}
|
||||
|
||||
this.headers[headerName] = headerValue;
|
||||
}
|
||||
|
||||
public bool TryGetHeaderValue(string headerName, out byte[] headerValue)
|
||||
{
|
||||
headerValue = null;
|
||||
|
||||
if (this.headers == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return this.headers.TryGetValue(headerName, out headerValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors.Communication
|
||||
{
|
||||
internal class ActorResponseMessage : IActorResponseMessage
|
||||
{
|
||||
private readonly IActorResponseMessageHeader header;
|
||||
private readonly IActorResponseMessageBody msgBody;
|
||||
|
||||
public ActorResponseMessage(
|
||||
IActorResponseMessageHeader header,
|
||||
IActorResponseMessageBody msgBody)
|
||||
{
|
||||
this.header = header;
|
||||
this.msgBody = msgBody;
|
||||
}
|
||||
|
||||
public IActorResponseMessageHeader GetHeader()
|
||||
{
|
||||
return this.header;
|
||||
}
|
||||
|
||||
public IActorResponseMessageBody GetBody()
|
||||
{
|
||||
return this.msgBody;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors.Communication
|
||||
{
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
[DataContract(Name = "msgResponse", Namespace = Constants.Namespace)]
|
||||
internal class ActorResponseMessageBody : IActorResponseMessageBody
|
||||
{
|
||||
[DataMember]
|
||||
private object response;
|
||||
|
||||
public void Set(object response)
|
||||
{
|
||||
this.response = response;
|
||||
}
|
||||
|
||||
public object Get(Type paramType)
|
||||
{
|
||||
return this.response;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors.Communication
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Runtime.Serialization;
|
||||
using Microsoft.Actions.Actors.Resources;
|
||||
|
||||
[DataContract(Name = "ActorResponseMessageHeaders", Namespace = Constants.Namespace)]
|
||||
|
||||
internal class ActorResponseMessageHeader : IActorResponseMessageHeader
|
||||
{
|
||||
[DataMember(Name = "Headers", IsRequired = true, Order = 2)]
|
||||
private readonly Dictionary<string, byte[]> headers;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ActorResponseMessageHeader"/> class.
|
||||
/// </summary>
|
||||
public ActorResponseMessageHeader()
|
||||
{
|
||||
this.headers = new Dictionary<string, byte[]>();
|
||||
}
|
||||
|
||||
public void AddHeader(string headerName, byte[] headerValue)
|
||||
{
|
||||
if (this.headers.ContainsKey(headerName))
|
||||
{
|
||||
// TODO throw actions specific translated exception type
|
||||
throw new System.Exception(
|
||||
string.Format(
|
||||
CultureInfo.CurrentCulture,
|
||||
SR.ErrorHeaderAlreadyExists,
|
||||
headerName));
|
||||
}
|
||||
|
||||
this.headers[headerName] = headerValue;
|
||||
}
|
||||
|
||||
public bool CheckIfItsEmpty()
|
||||
{
|
||||
if (this.headers == null || this.headers.Count == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool TryGetHeaderValue(string headerName, out byte[] headerValue)
|
||||
{
|
||||
headerValue = null;
|
||||
|
||||
if (this.headers == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return this.headers.TryGetValue(headerName, out headerValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors.Communication
|
||||
{
|
||||
internal class CacheEntry
|
||||
{
|
||||
public CacheEntry(
|
||||
IActorRequestMessageBodySerializer requestBodySerializer,
|
||||
IActorResponseMessageBodySerializer responseBodySerializer)
|
||||
{
|
||||
this.RequestMessageBodySerializer = requestBodySerializer;
|
||||
this.ResponseMessageBodySerializer = responseBodySerializer;
|
||||
}
|
||||
|
||||
public IActorRequestMessageBodySerializer RequestMessageBodySerializer { get; }
|
||||
|
||||
public IActorResponseMessageBodySerializer ResponseMessageBodySerializer { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors.Communication.Client
|
||||
{
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
internal class ActorNonRemotingClient
|
||||
{
|
||||
private readonly IActionsInteractor actionsInteractor;
|
||||
|
||||
public ActorNonRemotingClient(IActionsInteractor actionsInteractor)
|
||||
{
|
||||
this.actionsInteractor = actionsInteractor;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes an Actor method on Actions without remoting.
|
||||
/// </summary>
|
||||
/// <param name="actorType">Type of actor.</param>
|
||||
/// <param name="actorId">ActorId.</param>
|
||||
/// <param name="methodName">Method name to invoke.</param>
|
||||
/// <param name="jsonPayload">Serialized body.</param>
|
||||
/// <param name="cancellationToken">Cancels the operation.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
public Task<Stream> InvokeActorMethodWithoutRemotingAsync(string actorType, string actorId, string methodName, string jsonPayload, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return this.actionsInteractor.InvokeActorMethodWithoutRemotingAsync(actorType, actorId, methodName, jsonPayload, cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors.Communication.Client
|
||||
{
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
internal class ActorRemotingClient
|
||||
{
|
||||
private readonly ActorMessageSerializersManager serializersManager;
|
||||
private readonly IActorMessageBodyFactory remotingMessageBodyFactory = null;
|
||||
private readonly IActionsInteractor actionsInteractor;
|
||||
|
||||
public ActorRemotingClient(
|
||||
IActionsInteractor actionsInteractor,
|
||||
IActorMessageBodySerializationProvider serializationProvider = null)
|
||||
{
|
||||
this.actionsInteractor = actionsInteractor;
|
||||
this.serializersManager = IntializeSerializationManager(serializationProvider);
|
||||
this.remotingMessageBodyFactory = this.serializersManager.GetSerializationProvider().CreateMessageBodyFactory();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a factory for creating the remoting message bodies.
|
||||
/// </summary>
|
||||
/// <returns>A factory for creating the remoting message bodies.</returns>
|
||||
public IActorMessageBodyFactory GetRemotingMessageBodyFactory()
|
||||
{
|
||||
return this.remotingMessageBodyFactory;
|
||||
}
|
||||
|
||||
public async Task<IActorResponseMessage> InvokeAsync(
|
||||
IActorRequestMessage remotingRequestMessage,
|
||||
string methodName,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
return await this.actionsInteractor.InvokeActorMethodWithRemotingAsync(this.serializersManager, remotingRequestMessage, cancellationToken);
|
||||
}
|
||||
|
||||
private static ActorMessageSerializersManager IntializeSerializationManager(
|
||||
IActorMessageBodySerializationProvider serializationProvider)
|
||||
{
|
||||
// TODO serializer settings
|
||||
return new ActorMessageSerializersManager(
|
||||
serializationProvider,
|
||||
new ActorMessageHeaderSerializer());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors.Communication
|
||||
{
|
||||
internal class DataContractMessageFactory : IActorMessageBodyFactory
|
||||
{
|
||||
public IActorRequestMessageBody CreateRequestMessageBody(string interfaceName, string methodName, int numberOfParameters, object wrappedRequestObject)
|
||||
{
|
||||
return new ActorRequestMessageBody(numberOfParameters);
|
||||
}
|
||||
|
||||
public IActorResponseMessageBody CreateResponseMessageBody(string interfaceName, string methodName, object wrappedResponseObject)
|
||||
{
|
||||
return new ActorResponseMessageBody();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors.Communication
|
||||
{
|
||||
using System.IO;
|
||||
|
||||
/// <summary>
|
||||
/// Wraps an implementation of Stream and provides an idempotent impplementation of Dispose.
|
||||
/// </summary>
|
||||
internal class DisposableStream : Stream
|
||||
{
|
||||
private readonly Stream streamImplementation;
|
||||
private bool disposed;
|
||||
|
||||
public DisposableStream(Stream streamImplementation)
|
||||
{
|
||||
this.streamImplementation = streamImplementation;
|
||||
}
|
||||
|
||||
public override bool CanRead
|
||||
{
|
||||
get { return this.streamImplementation.CanRead; }
|
||||
}
|
||||
|
||||
public override bool CanSeek
|
||||
{
|
||||
get { return this.streamImplementation.CanSeek; }
|
||||
}
|
||||
|
||||
public override bool CanWrite
|
||||
{
|
||||
get { return this.streamImplementation.CanWrite; }
|
||||
}
|
||||
|
||||
public override long Length
|
||||
{
|
||||
get { return this.streamImplementation.Length; }
|
||||
}
|
||||
|
||||
public override long Position
|
||||
{
|
||||
get { return this.streamImplementation.Position; }
|
||||
set { this.streamImplementation.Position = value; }
|
||||
}
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
this.streamImplementation.Flush();
|
||||
}
|
||||
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
return this.streamImplementation.Seek(offset, origin);
|
||||
}
|
||||
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
this.streamImplementation.SetLength(value);
|
||||
}
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
return this.streamImplementation.Read(buffer, offset, count);
|
||||
}
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
this.streamImplementation.Write(buffer, offset, count);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (!this.disposed)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
if (disposing)
|
||||
{
|
||||
this.streamImplementation.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
this.disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors.Communication
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines the interface that must be implemented for providing factory for creating actor request body and response body objects.
|
||||
/// </summary>
|
||||
public interface IActorMessageBodyFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a actor request message body.
|
||||
/// </summary>
|
||||
/// <param name="interfaceName"> This is FullName for the service interface for which request body is being constructed.</param>
|
||||
/// <param name="methodName">MethodName for the service interface for which request will be sent to.</param>
|
||||
/// <param name="numberOfParameters">Number of Parameters in that Method.</param>
|
||||
/// <param name="wrappedRequestObject">Wrapped Request Object.</param>
|
||||
/// <returns>IActorRequestMessageBody.</returns>
|
||||
IActorRequestMessageBody CreateRequestMessageBody(string interfaceName, string methodName, int numberOfParameters, object wrappedRequestObject);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a actor response message body.
|
||||
/// </summary>
|
||||
/// <param name="interfaceName"> This is FullName for the service interface for which request body is being constructed.</param>
|
||||
/// <param name="methodName">MethodName for the service interface for which request will be sent to.</param>
|
||||
/// <param name="wrappedResponseObject">Wrapped Response Object.</param>
|
||||
/// <returns>IActorResponseMessageBody.</returns>
|
||||
IActorResponseMessageBody CreateResponseMessageBody(string interfaceName, string methodName, object wrappedResponseObject);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors.Communication
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
/// <summary>
|
||||
/// Defines the interface that must be implemented for providing custom serialization for the remoting request.
|
||||
/// </summary>
|
||||
internal interface IActorMessageBodySerializationProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Create a IServiceRemotingMessageBodyFactory used for creating remoting request and response body.
|
||||
/// </summary>
|
||||
/// <returns>A custom <see cref="IActorMessageBodyFactory"/> that can be used for creating remoting request and response message bodies.</returns>
|
||||
IActorMessageBodyFactory CreateMessageBodyFactory();
|
||||
|
||||
/// <summary>
|
||||
/// Creates IActorRequestMessageBodySerializer for a serviceInterface using Wrapped Message DataContract implementation.
|
||||
/// </summary>
|
||||
/// <param name="serviceInterfaceType">The remoted service interface.</param>
|
||||
/// <param name="methodRequestParameterTypes">The union of parameter types of all of the methods of the specified interface.</param>
|
||||
/// <param name="wrappedRequestMessageTypes">Wrapped Request Types for all Methods.</param>
|
||||
/// <returns>
|
||||
/// An instance of the <see cref="IActorRequestMessageBodySerializer" /> that can serialize the service
|
||||
/// actor request message body to a messaging body for transferring over the transport.
|
||||
/// </returns>
|
||||
IActorRequestMessageBodySerializer CreateRequestMessageBodySerializer(
|
||||
Type serviceInterfaceType,
|
||||
IEnumerable<Type> methodRequestParameterTypes,
|
||||
IEnumerable<Type> wrappedRequestMessageTypes = null);
|
||||
|
||||
/// <summary>
|
||||
/// Creates IActorResponseMessageBodySerializer for a serviceInterface using Wrapped Message DataContract implementation.
|
||||
/// </summary>
|
||||
/// <param name="serviceInterfaceType">The remoted service interface.</param>
|
||||
/// <param name="methodReturnTypes">The return types of all of the methods of the specified interface.</param>
|
||||
/// <param name="wrappedResponseMessageTypes">Wrapped Response Types for all remoting methods.</param>
|
||||
/// <returns>
|
||||
/// An instance of the <see cref="IActorResponseMessageBodySerializer" /> that can serialize the service
|
||||
/// actor response message body to a messaging body for transferring over the transport.
|
||||
/// </returns>
|
||||
IActorResponseMessageBodySerializer CreateResponseMessageBodySerializer(
|
||||
Type serviceInterfaceType,
|
||||
IEnumerable<Type> methodReturnTypes,
|
||||
IEnumerable<Type> wrappedResponseMessageTypes = null);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors.Communication
|
||||
{
|
||||
using System.IO;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a serializer that can serialize remoting layer message header to messaging layer header.
|
||||
/// </summary>
|
||||
internal interface IActorMessageHeaderSerializer
|
||||
{
|
||||
/// <summary>
|
||||
/// Serializes the remoting request message header to a message header.
|
||||
/// </summary>
|
||||
/// <param name="serviceRemotingRequestMessageHeader">Remoting header to serialize.</param>
|
||||
/// <returns>Serialized bytes.</returns>
|
||||
byte[] SerializeRequestHeader(IActorRequestMessageHeader serviceRemotingRequestMessageHeader);
|
||||
|
||||
/// <summary>
|
||||
/// Deserializes a request message header in to remoting header.
|
||||
/// </summary>
|
||||
/// <param name="messageHeader">Messaging layer header to be deserialized.</param>
|
||||
/// <returns>An <see cref="IActorRequestMessageHeader"/> that has the deserialized contents of the specified message header.</returns>
|
||||
IActorRequestMessageHeader DeserializeRequestHeaders(Stream messageHeader);
|
||||
|
||||
/// <summary>
|
||||
/// Serializes the remoting response message header to a message header.
|
||||
/// </summary>
|
||||
/// <param name="serviceRemotingResponseMessageHeader">Remoting header to serialize.</param>
|
||||
/// <returns>Serialized bytes.</returns>
|
||||
byte[] SerializeResponseHeader(IActorResponseMessageHeader serviceRemotingResponseMessageHeader);
|
||||
|
||||
/// <summary>
|
||||
/// Deserializes a response message header in to remoting header.
|
||||
/// </summary>
|
||||
/// <param name="messageHeader">Messaging layer header to be deserialized.</param>
|
||||
/// <returns>An <see cref="IActorRequestMessageHeader"/> that has the deserialized contents of the specified message header.</returns>
|
||||
IActorResponseMessageHeader DeserializeResponseHeaders(Stream messageHeader);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors.Communication
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines the interface that must be implemented for create Actor Request Message.
|
||||
/// </summary>
|
||||
public interface IActorRequestMessage
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the Actor Request Message Header.
|
||||
/// </summary>
|
||||
/// <returns>IActorRequestMessageHeader.</returns>
|
||||
IActorRequestMessageHeader GetHeader();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Actor Request Message Body.</summary>
|
||||
/// <returns>IActorRequestMessageBody.</returns>
|
||||
IActorRequestMessageBody GetBody();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors.Communication
|
||||
{
|
||||
using System;
|
||||
|
||||
/// <summary>
|
||||
/// Defines the interface that must be implemented to provide Request Message Body for remoting requests .
|
||||
/// This contains all the parameters remoting method has.
|
||||
/// </summary>
|
||||
public interface IActorRequestMessageBody
|
||||
{
|
||||
/// <summary>
|
||||
/// This Api gets called to set remoting method parameters before serializing/dispatching the request.
|
||||
/// </summary>
|
||||
/// <param name="position">Position of the parameter in Remoting Method.</param>
|
||||
/// <param name="parameName">Parameter Name in the Remoting Method.</param>
|
||||
/// <param name="parameter">Parameter Value.</param>
|
||||
void SetParameter(int position, string parameName, object parameter);
|
||||
|
||||
/// <summary>
|
||||
/// This is used to retrive parameter from request body before dispatching to service remoting method.
|
||||
/// </summary>
|
||||
/// <param name="position">Position of the parameter in Remoting Method.</param>
|
||||
/// <param name="parameName">Parameter Name in the Remoting Method.</param>
|
||||
/// <param name="paramType">Parameter Type.</param>
|
||||
/// <returns>The parameter that is at the specified position and has the specified name.</returns>
|
||||
object GetParameter(int position, string parameName, Type paramType);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors.Communication
|
||||
{
|
||||
using System.IO;
|
||||
|
||||
/// <summary>
|
||||
/// Defines the interface that must be implemented to provide a serializer/deserializer for remoting request message body.
|
||||
/// </summary>
|
||||
public interface IActorRequestMessageBodySerializer
|
||||
{
|
||||
/// <summary>
|
||||
/// Serialize the remoting request body object to a message body that can be sent over the wire.
|
||||
/// </summary>
|
||||
/// <param name="actorRequestMessageBody">Actor request message body object.</param>
|
||||
/// <returns>Serialized message body.</returns>
|
||||
byte[] Serialize(IActorRequestMessageBody actorRequestMessageBody);
|
||||
|
||||
/// <summary>
|
||||
/// Deserializes an incoming message body to actor request body object.
|
||||
/// </summary>
|
||||
/// <param name="messageBody">Serialized message body.</param>
|
||||
/// <returns>Deserialized remoting request message body object.</returns>
|
||||
IActorRequestMessageBody Deserialize(Stream messageBody);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors.Communication
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies the headers that are sent along with a request message.
|
||||
/// </summary>
|
||||
public interface IActorRequestMessageHeader
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the actorId to which remoting request will dispatch to.
|
||||
/// </summary>
|
||||
ActorId ActorId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the actorType to which remoting request will dispatch to.
|
||||
/// </summary>
|
||||
string ActorType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the call context which is used to limit re-eentrancy in Actors.
|
||||
/// </summary>
|
||||
string CallContext { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the methodId of the remote method.
|
||||
/// </summary>
|
||||
/// <value>The method id.</value>
|
||||
int MethodId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the interface id of the remote interface.
|
||||
/// </summary>
|
||||
/// <value>The interface id.</value>
|
||||
int InterfaceId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the identifier for the remote method invocation.
|
||||
/// </summary>
|
||||
string InvocationId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Method Name of the remoting method.
|
||||
/// </summary>
|
||||
string MethodName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new header with the specified name and value.
|
||||
/// </summary>
|
||||
/// <param name="headerName">The header Name.</param>
|
||||
/// <param name="headerValue">The header value.</param>
|
||||
void AddHeader(string headerName, byte[] headerValue);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the header with the specified name.
|
||||
/// </summary>
|
||||
/// <param name="headerName">The header Name.</param>
|
||||
/// <param name="headerValue">The header value.</param>
|
||||
/// <returns>true if a header with that name exists; otherwise, false.</returns>
|
||||
bool TryGetHeaderValue(string headerName, out byte[] headerValue);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors.Communication
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines an interface that must be implemented to provide a actor response message for remoting Api.
|
||||
/// </summary>
|
||||
public interface IActorResponseMessage
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the header of the response message.
|
||||
/// </summary>
|
||||
/// <returns>The header of this response message.</returns>
|
||||
IActorResponseMessageHeader GetHeader();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the body of the response message.
|
||||
/// </summary>
|
||||
/// <returns>The body of this response message.</returns>
|
||||
IActorResponseMessageBody GetBody();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors.Communication
|
||||
{
|
||||
using System;
|
||||
|
||||
/// <summary>
|
||||
/// Defines the interface that must be implemented to provide Request Message Body for remoting requests .
|
||||
/// This contains all the parameters remoting method has.
|
||||
/// </summary>
|
||||
public interface IActorResponseMessageBody
|
||||
{
|
||||
/// <summary>
|
||||
/// Sets the response of a remoting Method in a remoting response Body.
|
||||
/// </summary>
|
||||
/// <param name="response">Remoting Method Response.</param>
|
||||
void Set(object response);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the response of a remoting Method from a remoting response body before sending it to Client.
|
||||
/// </summary>
|
||||
/// <param name="paramType"> Return Type of a Remoting Method.</param>
|
||||
/// <returns>Remoting Method Response.</returns>
|
||||
object Get(Type paramType);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors.Communication
|
||||
{
|
||||
using System.IO;
|
||||
|
||||
/// <summary>
|
||||
/// Defines the interface that must be implemented to provide a serializer/deserializer for actor response message body.
|
||||
/// </summary>
|
||||
public interface IActorResponseMessageBodySerializer
|
||||
{
|
||||
/// <summary>
|
||||
/// Serialize the actor response body object to a message body that can be sent over the wire.
|
||||
/// </summary>
|
||||
/// <param name="actorResponseMessageBody">Actor request message body object.</param>
|
||||
/// <returns>Serialized message body.</returns>
|
||||
byte[] Serialize(IActorResponseMessageBody actorResponseMessageBody);
|
||||
|
||||
/// <summary>
|
||||
/// Deserializes an incoming message body to remoting response body object.
|
||||
/// </summary>
|
||||
/// <param name="messageBody">Serialized message body.</param>
|
||||
/// <returns>Deserialized actor response message body object.</returns>
|
||||
IActorResponseMessageBody Deserialize(Stream messageBody);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors.Communication
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines an interfaces that must be implemented to provide header for remoting response message.
|
||||
///
|
||||
/// </summary>
|
||||
public interface IActorResponseMessageHeader
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds a new header with the specified name and value.
|
||||
/// </summary>
|
||||
/// <param name="headerName">The header Name.</param>
|
||||
/// <param name="headerValue">The header value.</param>
|
||||
void AddHeader(string headerName, byte[] headerValue);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the header with the specified name.
|
||||
/// </summary>
|
||||
/// <param name="headerName">The header Name.</param>
|
||||
/// <param name="headerValue">The header value.</param>
|
||||
/// <returns>true if a header with that name exists; otherwise, false.</returns>
|
||||
bool TryGetHeaderValue(string headerName, out byte[] headerValue);
|
||||
|
||||
/// <summary>
|
||||
/// Return true if no header exists , else false.
|
||||
/// </summary>
|
||||
/// <returns>true or false.</returns>
|
||||
bool CheckIfItsEmpty();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,219 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors.Communication
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Runtime.Serialization.Formatters;
|
||||
using System.Runtime.Serialization.Formatters.Binary;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
using Microsoft.Actions.Actors;
|
||||
using Microsoft.Actions.Actors.Communication;
|
||||
using Microsoft.Actions.Actors.Resources;
|
||||
|
||||
/// <summary>
|
||||
/// Fault type used by Service Remoting to transfer the exception details from the service to the client.
|
||||
/// </summary>
|
||||
[DataContract(Name = "RemoteException", Namespace = Constants.Namespace)]
|
||||
internal class RemoteException
|
||||
{
|
||||
private static readonly DataContractSerializer ServiceExceptionDataSerializer = new DataContractSerializer(typeof(ServiceExceptionData));
|
||||
|
||||
private static BinaryFormatter binaryFormatter;
|
||||
|
||||
static RemoteException()
|
||||
{
|
||||
binaryFormatter = new BinaryFormatter
|
||||
{
|
||||
AssemblyFormat = FormatterAssemblyStyle.Simple,
|
||||
};
|
||||
}
|
||||
|
||||
public RemoteException(List<ArraySegment<byte>> buffers)
|
||||
{
|
||||
this.Data = buffers;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets serialized exception or the exception message encoded as UTF8 (if the exception cannot be serialized).
|
||||
/// </summary>
|
||||
/// <value>Serialized exception or exception message.</value>
|
||||
[DataMember(Name = "Data", Order = 0)]
|
||||
public List<ArraySegment<byte>> Data { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Factory method that constructs the RemoteException from an exception.
|
||||
/// </summary>
|
||||
/// <param name="exception">Exception.</param>
|
||||
/// <returns>Serialized bytes.</returns>
|
||||
public static byte[] FromException(Exception exception)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var stream = new MemoryStream())
|
||||
{
|
||||
binaryFormatter.Serialize(stream, exception);
|
||||
stream.Flush();
|
||||
return stream.ToArray();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// failed to serialize the exception, include the information about the exception in the data
|
||||
// Add trace diagnostics
|
||||
ActorTrace.Instance.WriteWarning(
|
||||
"RemoteException",
|
||||
"Serialization failed for Exception Type {0} : Reason {1}",
|
||||
exception.GetType().FullName,
|
||||
e);
|
||||
return FromExceptionString(exception);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the exception from the RemoteException.
|
||||
/// </summary>
|
||||
/// <param name="bufferedStream">The stream that contains the serialized exception or exception message.</param>
|
||||
/// <param name="result">Exception from the remote side.</param>
|
||||
/// <returns>true if there was a valid exception, false otherwise.</returns>
|
||||
public static bool ToException(Stream bufferedStream, out Exception result)
|
||||
{
|
||||
// try to de-serialize the bytes in to the exception
|
||||
if (TryDeserializeException(bufferedStream, out var res))
|
||||
{
|
||||
result = res;
|
||||
return true;
|
||||
}
|
||||
|
||||
// try to de-serialize the bytes in to exception requestMessage and create service exception
|
||||
if (TryDeserializeServiceException(bufferedStream, out result))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Set Reason for Serialization failure. This can happen in case where serialization succeded
|
||||
// but deserialization fails as type is not accessible
|
||||
result = res;
|
||||
bufferedStream.Dispose();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static bool TryDeserializeExceptionData(Stream data, out ServiceExceptionData result)
|
||||
{
|
||||
try
|
||||
{
|
||||
var exceptionData = (ServiceExceptionData)DeserializeServiceExceptionData(data);
|
||||
result = exceptionData;
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// swallowing the exception
|
||||
ActorTrace.Instance.WriteWarning(
|
||||
"RemoteException",
|
||||
" ServiceExceptionData DeSerialization failed : Reason {0}",
|
||||
e);
|
||||
}
|
||||
|
||||
result = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static byte[] FromExceptionString(Exception exception)
|
||||
{
|
||||
var exceptionStringBuilder = new StringBuilder();
|
||||
|
||||
exceptionStringBuilder.AppendFormat(
|
||||
CultureInfo.CurrentCulture,
|
||||
SR.ErrorExceptionSerializationFailed1,
|
||||
exception.GetType().FullName);
|
||||
|
||||
exceptionStringBuilder.AppendLine();
|
||||
|
||||
exceptionStringBuilder.AppendFormat(
|
||||
CultureInfo.CurrentCulture,
|
||||
SR.ErrorExceptionSerializationFailed2,
|
||||
exception);
|
||||
var exceptionData = new ServiceExceptionData(exception.GetType().FullName, exceptionStringBuilder.ToString());
|
||||
|
||||
var exceptionBytes = SerializeServiceExceptionData(exceptionData);
|
||||
|
||||
return exceptionBytes;
|
||||
}
|
||||
|
||||
private static bool TryDeserializeException(Stream data, out Exception result)
|
||||
{
|
||||
try
|
||||
{
|
||||
result = (Exception)binaryFormatter.Deserialize(data);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// return reason for serialization failure
|
||||
result = ex;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool TryDeserializeServiceException(Stream data, out Exception result)
|
||||
{
|
||||
try
|
||||
{
|
||||
data.Seek(0, SeekOrigin.Begin);
|
||||
if (TryDeserializeExceptionData(data, out var eData))
|
||||
{
|
||||
result = new ServiceException(eData.Type, eData.Message);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// swallowing the exception
|
||||
ActorTrace.Instance.WriteWarning("RemoteException", "DeSerialization failed : Reason {0}", e);
|
||||
}
|
||||
|
||||
result = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static object DeserializeServiceExceptionData(Stream buffer)
|
||||
{
|
||||
if ((buffer == null) || (buffer.Length == 0))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
using (var reader = XmlDictionaryReader.CreateBinaryReader(buffer, XmlDictionaryReaderQuotas.Max))
|
||||
{
|
||||
return ServiceExceptionDataSerializer.ReadObject(reader);
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] SerializeServiceExceptionData(ServiceExceptionData msg)
|
||||
{
|
||||
if (msg == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
using (var stream = new MemoryStream())
|
||||
{
|
||||
using (var writer = XmlDictionaryWriter.CreateBinaryWriter(stream))
|
||||
{
|
||||
ServiceExceptionDataSerializer.WriteObject(writer, msg);
|
||||
writer.Flush();
|
||||
return stream.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors.Communication
|
||||
{
|
||||
using System;
|
||||
|
||||
/// <summary>
|
||||
/// Provides an information about an exception from the service. This exception is thrown when the actual
|
||||
/// exception from the service cannot be serialized for transferring to client.
|
||||
/// </summary>
|
||||
public class ServiceException : Exception
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ServiceException"/> class.
|
||||
/// </summary>
|
||||
public ServiceException()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ServiceException" /> class with appropriate message.
|
||||
/// </summary>
|
||||
/// <param name="actualExceptionType">the ActualExceptionType of exception thrown.</param>
|
||||
/// <param name="message">The error message that explains the reason for this exception.
|
||||
/// </param>
|
||||
public ServiceException(string actualExceptionType, string message)
|
||||
: base(message)
|
||||
{
|
||||
this.ActualExceptionType = actualExceptionType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ActualExceptionType is the type of actual exception thrown.
|
||||
/// </summary>
|
||||
public string ActualExceptionType { get; private set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors.Communication
|
||||
{
|
||||
using System.Runtime.Serialization;
|
||||
using Microsoft.Actions.Actors;
|
||||
|
||||
[DataContract(Name = "ServiceExceptionData", Namespace = Constants.Namespace)]
|
||||
internal class ServiceExceptionData
|
||||
{
|
||||
public ServiceExceptionData(string type, string message)
|
||||
{
|
||||
this.Type = type;
|
||||
this.Message = message;
|
||||
}
|
||||
|
||||
[DataMember]
|
||||
public string Type { get; private set; }
|
||||
|
||||
[DataMember]
|
||||
public string Message { get; private set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
using System.Runtime.Serialization;
|
||||
using Microsoft.Actions.Actors;
|
||||
|
||||
/// <summary>
|
||||
/// This is a marker class indicating the remoting request / response is wrapped or not.
|
||||
/// </summary>
|
||||
[DataContract(Name = "msgBodywrapped", Namespace = Constants.Namespace)]
|
||||
public abstract class WrappedMessage
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the wrapped object.
|
||||
/// </summary>
|
||||
[DataMember(Name = "value", IsRequired = true, Order = 1)]
|
||||
public object Value
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
using Microsoft.Actions.Actors;
|
||||
using Microsoft.Actions.Actors.Communication;
|
||||
|
||||
[DataContract(Name = "WrappedMsgBody", Namespace = Constants.Namespace)]
|
||||
internal class WrappedMessageBody : WrappedMessage, IActorRequestMessageBody, IActorResponseMessageBody
|
||||
{
|
||||
public void SetParameter(
|
||||
int position,
|
||||
string parameName,
|
||||
object parameter)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public object GetParameter(
|
||||
int position,
|
||||
string parameName,
|
||||
Type paramType)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void Set(
|
||||
object response)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public object Get(
|
||||
Type paramType)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
using Microsoft.Actions.Actors.Communication;
|
||||
|
||||
internal class WrappedRequestMessageFactory : IActorMessageBodyFactory
|
||||
{
|
||||
public IActorRequestMessageBody CreateRequestMessageBody(string interfaceName, string methodName, int numberOfParameters, object wrappedRequestObject)
|
||||
{
|
||||
return new WrappedMessageBody()
|
||||
{
|
||||
Value = wrappedRequestObject,
|
||||
};
|
||||
}
|
||||
|
||||
public IActorResponseMessageBody CreateResponseMessageBody(string interfaceName, string methodName, object wrappedResponseObject)
|
||||
{
|
||||
return new WrappedMessageBody()
|
||||
{
|
||||
Value = wrappedResponseObject,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -8,21 +8,47 @@ namespace Microsoft.Actions.Actors
|
|||
/// <summary>
|
||||
/// Contains Constant string values used by the library.
|
||||
/// </summary>
|
||||
internal class Constants
|
||||
internal static class Constants
|
||||
{
|
||||
/// <summary>
|
||||
/// Constant string for request id in header.
|
||||
/// </summary>
|
||||
public const string RequestIdHeaderName = "X-ActionsRequestId";
|
||||
|
||||
/// <summary>
|
||||
/// Constant string for Environment Variable for Actions port.
|
||||
/// </summary>
|
||||
public const string RequestHeaderName = "X-ActionsRequestHeader";
|
||||
public const string ErrorResponseHeaderName = "X-ActionsErrorResponseHeader";
|
||||
public const string ActionsPortEnvironmentVariable = "ACTIONSPORT";
|
||||
public const string Actions = "actions";
|
||||
public const string Config = "config";
|
||||
public const string State = "state";
|
||||
public const string Actors = "actors";
|
||||
public const string Namespace = "urn:actors";
|
||||
public const string ActionsDefaultEndpoint = "localhost";
|
||||
public const string ActionsDefaultPort = "3500";
|
||||
public const string ActionsVersion = "v1.0";
|
||||
public const string Method = "method";
|
||||
public const string Reminders = "reminders";
|
||||
public const string Timers = "timers";
|
||||
|
||||
/// <summary>
|
||||
/// Constant string for Actors state management relative url.
|
||||
/// Gets string format for Actors state management relative url.
|
||||
/// </summary>
|
||||
public const string ActorStateManagementRelativeUrl = "actions/state";
|
||||
public static string ActorStateKeyRelativeUrlFormat => $"{ActionsVersion}/{Actors}/{{0}}/{{1}}/{State}/{{2}}";
|
||||
|
||||
/// <summary>
|
||||
/// Gets string format for Actors state management relative url.
|
||||
/// </summary>
|
||||
public static string ActorStateRelativeUrlFormat => $"{ActionsVersion}/{Actors}/{{0}}/{{1}}/{State}";
|
||||
|
||||
/// <summary>
|
||||
/// Gets string format for Actors method invocation relative url.
|
||||
/// </summary>
|
||||
public static string ActorMethodRelativeUrlFormat => $"{ActionsVersion}/{Actors}/{{0}}/{{1}}/{Method}/{{2}}";
|
||||
|
||||
/// <summary>
|
||||
/// Gets string format for Actors reminder registration relative url..
|
||||
/// </summary>
|
||||
public static string ActorReminderRelativeUrlFormat => $"{ActionsVersion}/{Actors}/{{0}}/{{1}}/{Reminders}/{{2}}";
|
||||
|
||||
/// <summary>
|
||||
/// Gets string format for Actors timer registration relative url..
|
||||
/// </summary>
|
||||
public static string ActorTimerRelativeUrlFormat => $"{ActionsVersion}/{Actors}/{{0}}/{{1}}/{Timers}/{{2}}";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,75 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors.Description
|
||||
{
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Reflection;
|
||||
using Microsoft.Actions.Actors;
|
||||
using Microsoft.Actions.Actors.Resources;
|
||||
using Microsoft.Actions.Actors.Runtime;
|
||||
|
||||
internal class ActorInterfaceDescription : InterfaceDescription
|
||||
{
|
||||
private ActorInterfaceDescription(Type actorInterfaceType, bool useCRCIdGeneration)
|
||||
: base("actor", actorInterfaceType, useCRCIdGeneration, MethodReturnCheck.EnsureReturnsTask)
|
||||
{
|
||||
}
|
||||
|
||||
public static ActorInterfaceDescription Create(Type actorInterfaceType)
|
||||
{
|
||||
EnsureActorInterface(actorInterfaceType);
|
||||
return new ActorInterfaceDescription(actorInterfaceType, false);
|
||||
}
|
||||
|
||||
public static ActorInterfaceDescription CreateUsingCRCId(Type actorInterfaceType)
|
||||
{
|
||||
EnsureActorInterface(actorInterfaceType);
|
||||
|
||||
return new ActorInterfaceDescription(actorInterfaceType, true);
|
||||
}
|
||||
|
||||
private static void EnsureActorInterface(Type actorInterfaceType)
|
||||
{
|
||||
if (!actorInterfaceType.GetTypeInfo().IsInterface)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
string.Format(
|
||||
CultureInfo.CurrentCulture,
|
||||
SR.ErrorNotAnActorInterface_InterfaceCheck,
|
||||
actorInterfaceType.FullName,
|
||||
typeof(IActor).FullName),
|
||||
"actorInterfaceType");
|
||||
}
|
||||
|
||||
var nonActorParentInterface = actorInterfaceType.GetNonActorParentType();
|
||||
if (nonActorParentInterface != null)
|
||||
{
|
||||
if (nonActorParentInterface == actorInterfaceType)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
string.Format(
|
||||
CultureInfo.CurrentCulture,
|
||||
SR.ErrorNotAnActorInterface_DerivationCheck1,
|
||||
actorInterfaceType.FullName,
|
||||
typeof(IActor).FullName),
|
||||
"actorInterfaceType");
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException(
|
||||
string.Format(
|
||||
CultureInfo.CurrentCulture,
|
||||
SR.ErrorNotAnActorInterface_DerivationCheck1,
|
||||
actorInterfaceType.FullName,
|
||||
nonActorParentInterface.FullName,
|
||||
typeof(IActor).FullName),
|
||||
"actorInterfaceType");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,226 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors.Description
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Reflection;
|
||||
using Microsoft.Actions.Actors.Common;
|
||||
using Microsoft.Actions.Actors.Resources;
|
||||
|
||||
internal abstract class InterfaceDescription
|
||||
{
|
||||
private readonly Type remotedInterfaceType;
|
||||
private readonly bool useCRCIdGeneration;
|
||||
private readonly int interfaceId;
|
||||
private readonly int interfaceIdV1;
|
||||
|
||||
private readonly MethodDescription[] methods;
|
||||
|
||||
protected InterfaceDescription(
|
||||
string remotedInterfaceKindName,
|
||||
Type remotedInterfaceType,
|
||||
bool useCRCIdGeneration,
|
||||
MethodReturnCheck methodReturnCheck = MethodReturnCheck.EnsureReturnsTask)
|
||||
{
|
||||
EnsureNotGeneric(remotedInterfaceKindName, remotedInterfaceType);
|
||||
|
||||
this.remotedInterfaceType = remotedInterfaceType;
|
||||
this.useCRCIdGeneration = useCRCIdGeneration;
|
||||
if (this.useCRCIdGeneration)
|
||||
{
|
||||
this.interfaceId = IdUtil.ComputeIdWithCRC(remotedInterfaceType);
|
||||
|
||||
// This is needed for backward compatibility support to V1 Stack like ActorEventproxy
|
||||
this.interfaceIdV1 = IdUtil.ComputeId(remotedInterfaceType);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.interfaceId = IdUtil.ComputeId(remotedInterfaceType);
|
||||
}
|
||||
|
||||
this.methods = GetMethodDescriptions(remotedInterfaceKindName, remotedInterfaceType, methodReturnCheck, useCRCIdGeneration);
|
||||
}
|
||||
|
||||
public int V1Id
|
||||
{
|
||||
get { return this.interfaceIdV1; }
|
||||
}
|
||||
|
||||
public int Id
|
||||
{
|
||||
get { return this.interfaceId; }
|
||||
}
|
||||
|
||||
public Type InterfaceType
|
||||
{
|
||||
get { return this.remotedInterfaceType; }
|
||||
}
|
||||
|
||||
public MethodDescription[] Methods
|
||||
{
|
||||
get { return this.methods; }
|
||||
}
|
||||
|
||||
private static void EnsureNotGeneric(
|
||||
string remotedInterfaceKindName,
|
||||
Type remotedInterfaceType)
|
||||
{
|
||||
if (remotedInterfaceType.GetTypeInfo().IsGenericType ||
|
||||
remotedInterfaceType.GetTypeInfo().IsGenericTypeDefinition)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
string.Format(
|
||||
CultureInfo.CurrentCulture,
|
||||
SR.ErrorRemotedInterfaceIsGeneric,
|
||||
remotedInterfaceKindName,
|
||||
remotedInterfaceType.FullName),
|
||||
remotedInterfaceKindName + "InterfaceType");
|
||||
}
|
||||
}
|
||||
|
||||
private static MethodDescription[] GetMethodDescriptions(
|
||||
string remotedInterfaceKindName,
|
||||
Type remotedInterfaceType,
|
||||
MethodReturnCheck methodReturnCheck,
|
||||
bool useCRCIdGeneration)
|
||||
{
|
||||
EnsureValidMethods(remotedInterfaceKindName, remotedInterfaceType, methodReturnCheck);
|
||||
var methods = remotedInterfaceType.GetMethods();
|
||||
var methodDescriptions = new MethodDescription[methods.Length];
|
||||
for (var i = 0; i < methods.Length; i++)
|
||||
{
|
||||
methodDescriptions[i] = MethodDescription.Create(remotedInterfaceKindName, methods[i], useCRCIdGeneration);
|
||||
}
|
||||
|
||||
return methodDescriptions;
|
||||
}
|
||||
|
||||
private static void EnsureValidMethods(
|
||||
string remotedInterfaceKindName,
|
||||
Type remotedInterfaceType,
|
||||
MethodReturnCheck methodReturnCheck)
|
||||
{
|
||||
var methodNameSet = new HashSet<string>();
|
||||
foreach (var m in remotedInterfaceType.GetMethods())
|
||||
{
|
||||
EnsureNotOverloaded(remotedInterfaceKindName, remotedInterfaceType, m, methodNameSet);
|
||||
EnsureNotGeneric(remotedInterfaceKindName, remotedInterfaceType, m);
|
||||
EnsureNotVariableArgs(remotedInterfaceKindName, remotedInterfaceType, m);
|
||||
|
||||
if (methodReturnCheck == MethodReturnCheck.EnsureReturnsTask)
|
||||
{
|
||||
EnsureReturnsTask(remotedInterfaceKindName, remotedInterfaceType, m);
|
||||
}
|
||||
|
||||
if (methodReturnCheck == MethodReturnCheck.EnsureReturnsVoid)
|
||||
{
|
||||
EnsureReturnsVoid(remotedInterfaceKindName, remotedInterfaceType, m);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void EnsureNotOverloaded(
|
||||
string remotedInterfaceKindName,
|
||||
Type remotedInterfaceType,
|
||||
MethodInfo methodInfo,
|
||||
ISet<string> methodNameSet)
|
||||
{
|
||||
if (methodNameSet.Contains(methodInfo.Name))
|
||||
{
|
||||
ThrowArgumentExceptionForMethodChecks(
|
||||
remotedInterfaceKindName,
|
||||
remotedInterfaceType,
|
||||
methodInfo,
|
||||
SR.ErrorRemotedMethodsIsOverloaded);
|
||||
}
|
||||
|
||||
methodNameSet.Add((methodInfo.Name));
|
||||
}
|
||||
|
||||
private static void EnsureNotGeneric(
|
||||
string remotedInterfaceKindName,
|
||||
Type remotedInterfaceType,
|
||||
MethodInfo methodInfo)
|
||||
{
|
||||
if (methodInfo.IsGenericMethod)
|
||||
{
|
||||
ThrowArgumentExceptionForMethodChecks(
|
||||
remotedInterfaceKindName,
|
||||
remotedInterfaceType,
|
||||
methodInfo,
|
||||
SR.ErrorRemotedMethodHasGenerics);
|
||||
}
|
||||
}
|
||||
|
||||
private static void EnsureNotVariableArgs(
|
||||
string remotedInterfaceKindName,
|
||||
Type remotedInterfaceType,
|
||||
MethodInfo methodInfo)
|
||||
{
|
||||
if (methodInfo.CallingConvention == CallingConventions.VarArgs)
|
||||
{
|
||||
ThrowArgumentExceptionForMethodChecks(
|
||||
remotedInterfaceKindName,
|
||||
remotedInterfaceType,
|
||||
methodInfo,
|
||||
SR.ErrorRemotedMethodHasVaArgs);
|
||||
}
|
||||
}
|
||||
|
||||
private static void EnsureReturnsTask(
|
||||
string remotedInterfaceKindName,
|
||||
Type remotedInterfaceType,
|
||||
MethodInfo methodInfo)
|
||||
{
|
||||
if (!TypeUtility.IsTaskType(methodInfo.ReturnType))
|
||||
{
|
||||
ThrowArgumentExceptionForMethodChecks(
|
||||
remotedInterfaceKindName,
|
||||
remotedInterfaceType,
|
||||
methodInfo,
|
||||
SR.ErrorRemotedMethodDoesNotReturnTask);
|
||||
}
|
||||
}
|
||||
|
||||
private static void EnsureReturnsVoid(
|
||||
string remotedInterfaceKindName,
|
||||
Type remotedInterfaceType,
|
||||
MethodInfo methodInfo)
|
||||
{
|
||||
if (!TypeUtility.IsVoidType(methodInfo.ReturnType))
|
||||
{
|
||||
throw new ArgumentException(
|
||||
string.Format(
|
||||
CultureInfo.CurrentCulture,
|
||||
SR.ErrorRemotedMethodDoesNotReturnVoid,
|
||||
remotedInterfaceKindName,
|
||||
methodInfo.Name,
|
||||
remotedInterfaceType.FullName,
|
||||
methodInfo.ReturnType.FullName,
|
||||
typeof(void)),
|
||||
remotedInterfaceKindName + "InterfaceType");
|
||||
}
|
||||
}
|
||||
|
||||
private static void ThrowArgumentExceptionForMethodChecks(
|
||||
string remotedInterfaceKindName,
|
||||
Type remotedInterfaceType,
|
||||
MethodInfo methodInfo,
|
||||
string resourceName)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
string.Format(
|
||||
CultureInfo.CurrentCulture,
|
||||
resourceName,
|
||||
remotedInterfaceKindName,
|
||||
methodInfo.Name,
|
||||
remotedInterfaceType.FullName),
|
||||
remotedInterfaceKindName + "InterfaceType");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors.Description
|
||||
{
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Reflection;
|
||||
using Microsoft.Actions.Actors.Resources;
|
||||
|
||||
internal sealed class MethodArgumentDescription
|
||||
{
|
||||
private readonly ParameterInfo parameterInfo;
|
||||
|
||||
private MethodArgumentDescription(ParameterInfo parameterInfo)
|
||||
{
|
||||
this.parameterInfo = parameterInfo;
|
||||
}
|
||||
|
||||
public string Name
|
||||
{
|
||||
get { return this.parameterInfo.Name; }
|
||||
}
|
||||
|
||||
public Type ArgumentType
|
||||
{
|
||||
get { return this.parameterInfo.ParameterType; }
|
||||
}
|
||||
|
||||
internal static MethodArgumentDescription Create(string remotedInterfaceKindName, MethodInfo methodInfo, ParameterInfo parameter)
|
||||
{
|
||||
var remotedInterfaceType = methodInfo.DeclaringType;
|
||||
EnsureNotOutRefOptional(remotedInterfaceKindName, remotedInterfaceType, methodInfo, parameter);
|
||||
EnsureNotVariableLength(remotedInterfaceKindName, remotedInterfaceType, methodInfo, parameter);
|
||||
|
||||
return new MethodArgumentDescription(parameter);
|
||||
}
|
||||
|
||||
private static void EnsureNotVariableLength(
|
||||
string remotedInterfaceKindName,
|
||||
Type remotedInterfaceType,
|
||||
MethodInfo methodInfo,
|
||||
ParameterInfo param)
|
||||
{
|
||||
if (param.GetCustomAttributes(typeof(ParamArrayAttribute), false).Length > 0)
|
||||
{
|
||||
ThrowArgumentExceptionForParamChecks(
|
||||
remotedInterfaceKindName,
|
||||
remotedInterfaceType,
|
||||
methodInfo,
|
||||
param,
|
||||
SR.ErrorRemotedMethodHasVaArgParameter);
|
||||
}
|
||||
}
|
||||
|
||||
private static void EnsureNotOutRefOptional(
|
||||
string remotedInterfaceKindName,
|
||||
Type remotedInterfaceType,
|
||||
MethodInfo methodInfo,
|
||||
ParameterInfo param)
|
||||
{
|
||||
if (param.IsOut || param.IsIn || param.IsOptional)
|
||||
{
|
||||
ThrowArgumentExceptionForParamChecks(
|
||||
remotedInterfaceKindName,
|
||||
remotedInterfaceType,
|
||||
methodInfo,
|
||||
param,
|
||||
SR.ErrorRemotedMethodHasOutRefOptionalParameter);
|
||||
}
|
||||
}
|
||||
|
||||
private static void ThrowArgumentExceptionForParamChecks(
|
||||
string remotedInterfaceKindName,
|
||||
Type remotedInterfaceType,
|
||||
MethodInfo methodInfo,
|
||||
ParameterInfo param,
|
||||
string resourceName)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
string.Format(
|
||||
CultureInfo.CurrentCulture,
|
||||
resourceName,
|
||||
remotedInterfaceKindName,
|
||||
methodInfo.Name,
|
||||
remotedInterfaceType.FullName,
|
||||
param.Name),
|
||||
remotedInterfaceKindName + "InterfaceType");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors.Description
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using Microsoft.Actions.Actors.Common;
|
||||
using Microsoft.Actions.Actors.Resources;
|
||||
|
||||
internal class MethodDescription
|
||||
{
|
||||
private readonly bool useCRCIdGeneration;
|
||||
|
||||
private MethodDescription(
|
||||
MethodInfo methodInfo,
|
||||
MethodArgumentDescription[] arguments,
|
||||
bool hasCancellationToken,
|
||||
bool useCRCIdGeneration)
|
||||
{
|
||||
this.MethodInfo = methodInfo;
|
||||
this.useCRCIdGeneration = useCRCIdGeneration;
|
||||
if (this.useCRCIdGeneration)
|
||||
{
|
||||
this.Id = IdUtil.ComputeIdWithCRC(methodInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.Id = IdUtil.ComputeId(methodInfo);
|
||||
}
|
||||
|
||||
this.Arguments = arguments;
|
||||
this.HasCancellationToken = hasCancellationToken;
|
||||
}
|
||||
|
||||
public int Id { get; }
|
||||
|
||||
public string Name
|
||||
{
|
||||
get { return this.MethodInfo.Name; }
|
||||
}
|
||||
|
||||
public Type ReturnType
|
||||
{
|
||||
get { return this.MethodInfo.ReturnType; }
|
||||
}
|
||||
|
||||
public bool HasCancellationToken { get; }
|
||||
|
||||
public MethodArgumentDescription[] Arguments { get; }
|
||||
|
||||
public MethodInfo MethodInfo { get; }
|
||||
|
||||
internal static MethodDescription Create(string remotedInterfaceKindName, MethodInfo methodInfo, bool useCRCIdGeneration)
|
||||
{
|
||||
var parameters = methodInfo.GetParameters();
|
||||
var argumentList = new List<MethodArgumentDescription>(parameters.Length);
|
||||
var hasCancellationToken = false;
|
||||
|
||||
foreach (var param in parameters)
|
||||
{
|
||||
if (hasCancellationToken)
|
||||
{
|
||||
// If the method has a cancellation token, then it must be the last argument.
|
||||
throw new ArgumentException(
|
||||
string.Format(
|
||||
CultureInfo.CurrentCulture,
|
||||
SR.ErrorRemotedMethodCancellationTokenOutOfOrder,
|
||||
remotedInterfaceKindName,
|
||||
methodInfo.Name,
|
||||
methodInfo.DeclaringType.FullName,
|
||||
param.Name,
|
||||
typeof(CancellationToken)),
|
||||
remotedInterfaceKindName + "InterfaceType");
|
||||
}
|
||||
|
||||
if (param.ParameterType == typeof(CancellationToken))
|
||||
{
|
||||
hasCancellationToken = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
argumentList.Add(MethodArgumentDescription.Create(remotedInterfaceKindName, methodInfo, param));
|
||||
}
|
||||
}
|
||||
|
||||
return new MethodDescription(
|
||||
methodInfo,
|
||||
argumentList.ToArray(),
|
||||
hasCancellationToken,
|
||||
useCRCIdGeneration);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors.Description
|
||||
{
|
||||
internal enum MethodReturnCheck
|
||||
{
|
||||
EnsureReturnsTask,
|
||||
EnsureReturnsVoid,
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors.Description
|
||||
{
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
internal static class TypeUtility
|
||||
{
|
||||
public static bool IsTaskType(Type type)
|
||||
{
|
||||
return ((type == typeof(Task)) ||
|
||||
(type.GetTypeInfo().IsGenericType && (type.GetGenericTypeDefinition() == typeof(Task<>))));
|
||||
}
|
||||
|
||||
public static bool IsVoidType(Type type)
|
||||
{
|
||||
return ((type == typeof(void)) || (type == null));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors
|
||||
{
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using Microsoft.Actions.Actors.Communication;
|
||||
|
||||
internal class Helper
|
||||
{
|
||||
public static string GetCallContext()
|
||||
{
|
||||
if (ActorLogicalCallContext.TryGet(out var callContextValue))
|
||||
{
|
||||
return string.Format(
|
||||
CultureInfo.InvariantCulture,
|
||||
"{0}{1}",
|
||||
callContextValue,
|
||||
Guid.NewGuid().ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
return Guid.NewGuid().ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,124 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Actions.Actors.Communication;
|
||||
using Microsoft.Actions.Actors.Runtime;
|
||||
|
||||
/// <summary>
|
||||
/// Interface for interacting with Actions runtime.
|
||||
/// </summary>
|
||||
internal interface IActionsInteractor
|
||||
{
|
||||
/// <summary>
|
||||
/// Invokes an Actor method on Actions without remoting.
|
||||
/// </summary>
|
||||
/// <param name="actorType">Type of actor.</param>
|
||||
/// <param name="actorId">ActorId.</param>
|
||||
/// <param name="methodName">Method name to invoke.</param>
|
||||
/// <param name="jsonPayload">Serialized body.</param>
|
||||
/// <param name="cancellationToken">Cancels the operation.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task<Stream> InvokeActorMethodWithoutRemotingAsync(string actorType, string actorId, string methodName, string jsonPayload, CancellationToken cancellationToken = default(CancellationToken));
|
||||
|
||||
/// <summary>
|
||||
/// Saves Actor state. This is temporary until the Actions runtime implements the Batch state update.
|
||||
/// </summary>
|
||||
/// <param name="actorType">Type of actor.</param>
|
||||
/// <param name="actorId">ActorId.</param>
|
||||
/// <param name="keyName">state name.</param>
|
||||
/// <param name="data">State to be saved.</param>
|
||||
/// <param name="cancellationToken">Cancels the operation.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task SaveStateAsync(string actorType, string actorId, string keyName, string data, CancellationToken cancellationToken = default(CancellationToken));
|
||||
|
||||
/// <summary>
|
||||
/// Removes Actor state. This is temporary until the Actions runtime implements the Batch state update.
|
||||
/// </summary>
|
||||
/// <param name="actorType">Type of actor.</param>
|
||||
/// <param name="actorId">ActorId.</param>
|
||||
/// <param name="keyName">state name.</param>
|
||||
/// <param name="cancellationToken">Cancels the operation.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task RemoveStateAsync(string actorType, string actorId, string keyName, CancellationToken cancellationToken = default(CancellationToken));
|
||||
|
||||
/// <summary>
|
||||
/// Saves state batch to Actions.
|
||||
/// </summary>
|
||||
/// <param name="actorType">Type of actor.</param>
|
||||
/// <param name="actorId">ActorId.</param>
|
||||
/// <param name="data">Json data with state changes as per the actions spec for transaction state update.</param>
|
||||
/// <param name="cancellationToken">Cancels the operation.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task SaveStateTransactionallyAsync(string actorType, string actorId, string data, CancellationToken cancellationToken = default(CancellationToken));
|
||||
|
||||
/// <summary>
|
||||
/// Saves a state to Actions.
|
||||
/// </summary>
|
||||
/// <param name="actorType">Type of actor.</param>
|
||||
/// <param name="actorId">ActorId.</param>
|
||||
/// <param name="keyName">Name of key to get value for.</param>
|
||||
/// <param name="cancellationToken">Cancels the operation.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
Task<byte[]> GetStateAsync(string actorType, string actorId, string keyName, CancellationToken cancellationToken = default(CancellationToken));
|
||||
|
||||
/// <summary>
|
||||
/// Invokes Actor method.
|
||||
/// </summary>
|
||||
/// <param name="serializersManager">Serializers manager for remoting calls.</param>
|
||||
/// <param name="remotingRequestRequestMessage">Actor Request Message.</param>
|
||||
/// <param name="cancellationToken">Cancels the operation.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the result of the asynchronous operation.</returns>
|
||||
Task<IActorResponseMessage> InvokeActorMethodWithRemotingAsync(ActorMessageSerializersManager serializersManager, IActorRequestMessage remotingRequestRequestMessage, CancellationToken cancellationToken = default(CancellationToken));
|
||||
|
||||
/// <summary>
|
||||
/// Register a reminder.
|
||||
/// </summary>
|
||||
/// <param name="actorType">Type of actor.</param>
|
||||
/// <param name="actorId">ActorId.</param>
|
||||
/// <param name="reminderName">Name of reminder to register.</param>
|
||||
/// <param name="data">Json reminder data as per the actions spec.</param>
|
||||
/// <param name="cancellationToken">Cancels the operation.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the result of the asynchronous operation.</returns>
|
||||
Task RegisterReminderAsync(string actorType, string actorId, string reminderName, string data, CancellationToken cancellationToken = default(CancellationToken));
|
||||
|
||||
/// <summary>
|
||||
/// Unregisters a reminder.
|
||||
/// </summary>
|
||||
/// <param name="actorType">Type of actor.</param>
|
||||
/// <param name="actorId">ActorId.</param>
|
||||
/// <param name="reminderName">Name of reminder to unregister.</param>
|
||||
/// <param name="cancellationToken">Cancels the operation.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the result of the asynchronous operation.</returns>
|
||||
Task UnregisterReminderAsync(string actorType, string actorId, string reminderName, CancellationToken cancellationToken = default(CancellationToken));
|
||||
|
||||
/// <summary>
|
||||
/// Registers a timer.
|
||||
/// </summary>
|
||||
/// <param name="actorType">Type of actor.</param>
|
||||
/// <param name="actorId">ActorId.</param>
|
||||
/// <param name="timerName">Name of timer to register.</param>
|
||||
/// <param name="data">Json reminder data as per the actions spec.</param>
|
||||
/// <param name="cancellationToken">Cancels the operation.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the result of the asynchronous operation.</returns>
|
||||
Task RegisterTimerAsync(string actorType, string actorId, string timerName, string data, CancellationToken cancellationToken = default(CancellationToken));
|
||||
|
||||
/// <summary>
|
||||
/// Unegisters a timer.
|
||||
/// </summary>
|
||||
/// <param name="actorType">Type of actor.</param>
|
||||
/// <param name="actorId">ActorId.</param>
|
||||
/// <param name="timerName">Name of timer to register.</param>
|
||||
/// <param name="cancellationToken">Cancels the operation.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the result of the asynchronous operation.</returns>
|
||||
Task UnregisterTimerAsync(string actorType, string actorId, string timerName, CancellationToken cancellationToken = default(CancellationToken));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors
|
||||
{
|
||||
using System;
|
||||
using Microsoft.Actions.Actors.Client;
|
||||
|
||||
/// <summary>
|
||||
/// Interface for ActorReference.
|
||||
/// </summary>
|
||||
internal interface IActorReference
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates an <see cref="ActorProxy"/> that implements an actor interface for the actor using the
|
||||
/// <see cref="ActorProxyFactory.CreateActorProxy(ActorId, Type, string)"/>
|
||||
/// method.
|
||||
/// </summary>
|
||||
/// <param name="actorInterfaceType">Actor interface for the created <see cref="ActorProxy"/> to implement.</param>
|
||||
/// <returns>An actor proxy object that implements <see cref="IActorProxy"/> and TActorInterface.</returns>
|
||||
object Bind(Type actorInterfaceType);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,642 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Xml;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
/// <summary>
|
||||
/// Defines extension methods for JsonReader.
|
||||
/// </summary>
|
||||
internal static class JsonReaderExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Moves the json reader token to next content.
|
||||
/// </summary>
|
||||
/// <param name="reader">The JsonReader.</param>
|
||||
public static void MoveToContent(this JsonReader reader)
|
||||
{
|
||||
while ((reader.TokenType == JsonToken.Comment || reader.TokenType == JsonToken.None) && reader.Read())
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads StartObject token, throws if its not.
|
||||
/// </summary>
|
||||
/// <param name="reader">The JsonReader.</param>
|
||||
public static void ReadStartObject(this JsonReader reader)
|
||||
{
|
||||
reader.MoveToContent();
|
||||
if (reader.TokenType != JsonToken.StartObject)
|
||||
{
|
||||
throw new JsonReaderException($"Unexpected JsonToken {reader.TokenType}.");
|
||||
}
|
||||
|
||||
reader.Read();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads EndObject token, throws if its not.
|
||||
/// </summary>
|
||||
/// <param name="reader">The JsonReader.</param>
|
||||
public static void ReadEndObject(this JsonReader reader)
|
||||
{
|
||||
reader.MoveToContent();
|
||||
if (reader.TokenType != JsonToken.EndObject)
|
||||
{
|
||||
throw new JsonReaderException($"Unexpected JsonToken {reader.TokenType}.");
|
||||
}
|
||||
|
||||
reader.Read();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads StartArray token, throws if its not.
|
||||
/// </summary>
|
||||
/// <param name="reader">The JsonReader.</param>
|
||||
public static void ReadStartArray(this JsonReader reader)
|
||||
{
|
||||
reader.MoveToContent();
|
||||
if (reader.TokenType != JsonToken.StartArray)
|
||||
{
|
||||
throw new JsonReaderException($"Unexpected JsonToken {reader.TokenType}.");
|
||||
}
|
||||
|
||||
reader.Read();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads EndArray token, throws if its not.
|
||||
/// </summary>
|
||||
/// <param name="reader">The JsonReader.</param>
|
||||
public static void ReadEndArray(this JsonReader reader)
|
||||
{
|
||||
reader.MoveToContent();
|
||||
if (reader.TokenType != JsonToken.EndArray)
|
||||
{
|
||||
throw new JsonReaderException($"Unexpected JsonToken {reader.TokenType}.");
|
||||
}
|
||||
|
||||
reader.Read();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads PropertyName token, throws if its not.
|
||||
/// </summary>
|
||||
/// <param name="reader">The JsonReader.</param>
|
||||
/// <returns>NameDescription of property.</returns>
|
||||
public static string ReadPropertyName(this JsonReader reader)
|
||||
{
|
||||
if (reader.TokenType != JsonToken.PropertyName)
|
||||
{
|
||||
throw new JsonReaderException($"Error reading Property NameDescription from Json, unexpected JsonToken {reader.TokenType}.");
|
||||
}
|
||||
|
||||
var propName = reader.Value?.ToString();
|
||||
reader.Read();
|
||||
return propName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of current JSON token as <see cref="string"/> and moves to next token".
|
||||
/// </summary>
|
||||
/// <param name="reader">Json reader.</param>
|
||||
/// <returns>A <see cref="string" />.</returns>
|
||||
public static string ReadValueAsString(this JsonReader reader)
|
||||
{
|
||||
string value = null;
|
||||
switch (reader.TokenType)
|
||||
{
|
||||
case JsonToken.String:
|
||||
value = (string)reader.Value;
|
||||
break;
|
||||
case JsonToken.Integer:
|
||||
case JsonToken.Float:
|
||||
case JsonToken.Boolean:
|
||||
case JsonToken.Date:
|
||||
var formattable = reader.Value as IFormattable;
|
||||
if (formattable != null)
|
||||
{
|
||||
value = formattable.ToString();
|
||||
}
|
||||
|
||||
break;
|
||||
case JsonToken.Null:
|
||||
// value is initialized to null
|
||||
break;
|
||||
default:
|
||||
throw new JsonReaderException($"Error reading string. Unexpected token: {reader.TokenType}.");
|
||||
}
|
||||
|
||||
reader.Read();
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of current JSON token as <see cref="bool"/> and moves to next token".
|
||||
/// </summary>
|
||||
/// <param name="reader">Json reader.</param>
|
||||
/// <returns>A <see cref="bool" />.</returns>
|
||||
public static bool? ReadValueAsBool(this JsonReader reader)
|
||||
{
|
||||
bool? value = null;
|
||||
|
||||
switch (reader.TokenType)
|
||||
{
|
||||
case JsonToken.Boolean:
|
||||
value = Convert.ToBoolean(reader.Value);
|
||||
break;
|
||||
case JsonToken.String:
|
||||
value = ParseAsType((string)reader.Value, bool.Parse);
|
||||
break;
|
||||
case JsonToken.Null:
|
||||
// value is initialized to null
|
||||
break;
|
||||
default:
|
||||
throw new JsonReaderException($"Error reading boolean. Unexpected token: {reader.TokenType}.");
|
||||
}
|
||||
|
||||
reader.Read();
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of current JSON token as <see cref="int"/> and moves to next token".
|
||||
/// </summary>
|
||||
/// <param name="reader">Json reader.</param>
|
||||
/// <returns>A <see cref="int" />.</returns>
|
||||
public static int? ReadValueAsInt(this JsonReader reader)
|
||||
{
|
||||
int? value = null;
|
||||
|
||||
switch (reader.TokenType)
|
||||
{
|
||||
case JsonToken.Integer:
|
||||
value = Convert.ToInt32(reader.Value);
|
||||
break;
|
||||
|
||||
case JsonToken.String:
|
||||
value = ParseAsTypeWithInvariantCulture((string)reader.Value, int.Parse);
|
||||
break;
|
||||
|
||||
case JsonToken.Null:
|
||||
// value is initialized to null
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new JsonReaderException($"Error reading integer. Unexpected token: {reader.TokenType}.");
|
||||
}
|
||||
|
||||
reader.Read();
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of current JSON token as <see cref="byte"/> and moves to next token".
|
||||
/// </summary>
|
||||
/// <param name="reader">Json reader.</param>
|
||||
/// <returns>A <see cref="int" />.</returns>
|
||||
public static byte ReadValueAsByte(this JsonReader reader)
|
||||
{
|
||||
// byte is int in json.
|
||||
int value;
|
||||
|
||||
switch (reader.TokenType)
|
||||
{
|
||||
case JsonToken.Integer:
|
||||
value = Convert.ToInt32(reader.Value);
|
||||
break;
|
||||
|
||||
case JsonToken.String:
|
||||
value = ParseAsTypeWithInvariantCulture((string)reader.Value, int.Parse);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new JsonReaderException($"Error reading integer. Unexpected token: {reader.TokenType}.");
|
||||
}
|
||||
|
||||
reader.Read();
|
||||
return (byte)value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of current JSON token as <see cref="long"/> and moves to next token".
|
||||
/// </summary>
|
||||
/// <param name="reader">Json reader.</param>
|
||||
/// <returns>A <see cref="long" />.</returns>
|
||||
public static long? ReadValueAsLong(this JsonReader reader)
|
||||
{
|
||||
long? value = null;
|
||||
|
||||
switch (reader.TokenType)
|
||||
{
|
||||
case JsonToken.Integer:
|
||||
value = Convert.ToInt64(reader.Value);
|
||||
break;
|
||||
case JsonToken.String:
|
||||
value = ParseAsTypeWithInvariantCulture((string)reader.Value, long.Parse);
|
||||
break;
|
||||
case JsonToken.Null:
|
||||
// value is initialized to null
|
||||
break;
|
||||
default:
|
||||
throw new JsonReaderException($"Error reading integer. Unexpected token: {reader.TokenType}.");
|
||||
}
|
||||
|
||||
reader.Read();
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of current JSON token as <see cref="double"/> and moves to next token".
|
||||
/// </summary>
|
||||
/// <param name="reader">Json reader.</param>
|
||||
/// <returns>A <see cref="double" />.</returns>
|
||||
public static double? ReadValueAsDouble(this JsonReader reader)
|
||||
{
|
||||
double? value = null;
|
||||
|
||||
switch (reader.TokenType)
|
||||
{
|
||||
case JsonToken.Float:
|
||||
value = Convert.ToDouble(reader.Value);
|
||||
break;
|
||||
case JsonToken.String:
|
||||
value = ParseAsTypeWithInvariantCulture((string)reader.Value, double.Parse);
|
||||
break;
|
||||
case JsonToken.Null:
|
||||
// value is initialized to null
|
||||
break;
|
||||
default:
|
||||
throw new JsonReaderException($"Error reading integer. Unexpected token: {reader.TokenType}.");
|
||||
}
|
||||
|
||||
reader.Read();
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of current JSON token as <see cref="System.DateTime"/> and moves to next token".
|
||||
/// </summary>
|
||||
/// <param name="reader">Json reader.</param>
|
||||
/// <returns>A <see cref="System.DateTime" />.</returns>
|
||||
public static DateTime? ReadValueAsDateTimeISO8601Format(this JsonReader reader)
|
||||
{
|
||||
// DateTime is a string in ISO8601 format
|
||||
DateTime? value = null;
|
||||
|
||||
switch (reader.TokenType)
|
||||
{
|
||||
case JsonToken.Date:
|
||||
value = (DateTime)reader.Value;
|
||||
break;
|
||||
case JsonToken.String:
|
||||
var valueString = (string)reader.Value;
|
||||
try
|
||||
{
|
||||
value = XmlConvert.ToDateTime(valueString, XmlDateTimeSerializationMode.Utc);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// TODO: try parsing with DateTime.Parse, Remove it once all apis return in ISO8601 format.
|
||||
try
|
||||
{
|
||||
value = DateTime.Parse(valueString);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new JsonReaderException(
|
||||
$"Error converting string to System.DateTime, string value to be converted is {valueString}. DateTime values must be specified in string as per ISO8601",
|
||||
ex);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case JsonToken.Null:
|
||||
// value is initialized to null
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new JsonReaderException($"Error reading Date. Unexpected token: {reader.TokenType}.");
|
||||
}
|
||||
|
||||
reader.Read();
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of current JSON token as <see cref="System.TimeSpan"/> and moves to next token".
|
||||
/// </summary>
|
||||
/// <param name="reader">Json reader.</param>
|
||||
/// <returns>A <see cref="System.TimeSpan" />.</returns>
|
||||
public static TimeSpan? ReadValueAsTimeSpanISO8601Format(this JsonReader reader)
|
||||
{
|
||||
// TimeSpan is a string in ISO8601 format
|
||||
TimeSpan? value = null;
|
||||
|
||||
switch (reader.TokenType)
|
||||
{
|
||||
case JsonToken.String:
|
||||
var valueString = (string)reader.Value;
|
||||
try
|
||||
{
|
||||
value = XmlConvert.ToTimeSpan(valueString);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// TODO: try parsing with DateTime.Parse, Remove it once all apis return in ISO8601 format.
|
||||
try
|
||||
{
|
||||
value = TimeSpan.Parse(valueString);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new JsonReaderException(
|
||||
$"Error converting string to System.TimeSpan, string value to be converted is {valueString}. Timespan values must be specified in string as per ISO8601.",
|
||||
ex);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
case JsonToken.Null:
|
||||
// value is initialized to null
|
||||
break;
|
||||
|
||||
default:
|
||||
{
|
||||
throw new JsonReaderException($"Error reading TimeSpan. Unexpected token: {reader.TokenType}.");
|
||||
}
|
||||
}
|
||||
|
||||
reader.Read();
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of current JSON token as <see cref="System.TimeSpan"/> and moves to next token".
|
||||
/// </summary>
|
||||
/// <param name="reader">Json reader.</param>
|
||||
/// <returns>A <see cref="System.TimeSpan" />.</returns>
|
||||
public static TimeSpan? ReadValueAsTimeSpanActionsFormat(this JsonReader reader)
|
||||
{
|
||||
// TimeSpan is a string. Format returned by Actions is: 1h4m5s4ms4us4ns
|
||||
// acceptable values are: m, s, ms, us(micro), ns
|
||||
TimeSpan? value = null;
|
||||
|
||||
switch (reader.TokenType)
|
||||
{
|
||||
case JsonToken.String:
|
||||
var valueString = (string)reader.Value;
|
||||
try
|
||||
{
|
||||
// Change the value returned by Actions runtime, so that it can be parsed with TimeSpan.
|
||||
// Format returned by Actions runtime: 4h15m50s60ms. It doesnt have days.
|
||||
// Actions runtime should handle timespans in ISO 8601 format.
|
||||
// Replace ms before m & s. Also aappend 0 days for parsing correctly with TimeSpan
|
||||
int hIndex = valueString.IndexOf('h');
|
||||
int mIndex = valueString.IndexOf('m');
|
||||
int sIndex = valueString.IndexOf('s');
|
||||
int msIndex = valueString.IndexOf("ms");
|
||||
|
||||
// handle days from hours.
|
||||
var hours = int.Parse(valueString.Substring(0, hIndex));
|
||||
var days = hours / 24;
|
||||
hours = hours % 24;
|
||||
|
||||
var minutes = int.Parse(valueString.Substring(hIndex + 1, mIndex - (hIndex + 1)));
|
||||
var seconds = int.Parse(valueString.Substring(mIndex + 1, sIndex - (mIndex + 1)));
|
||||
var miliseconds = int.Parse(valueString.Substring(sIndex + 1, msIndex - (sIndex + 1)));
|
||||
|
||||
value = new TimeSpan(days, hours, minutes, seconds, miliseconds);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new JsonReaderException(
|
||||
$"Error converting string to System.TimeSpan, string value to be converted is {valueString}..", ex);
|
||||
}
|
||||
|
||||
break;
|
||||
case JsonToken.Null:
|
||||
// value is initialized to null
|
||||
break;
|
||||
|
||||
default:
|
||||
{
|
||||
throw new JsonReaderException($"Error reading TimeSpan. Unexpected token: {reader.TokenType}.");
|
||||
}
|
||||
}
|
||||
|
||||
reader.Read();
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of current JSON token as <see cref="System.Guid"/> and moves to next token".
|
||||
/// </summary>
|
||||
/// <param name="reader">Json reader.</param>
|
||||
/// <returns>A <see cref="System.Guid" />.</returns>
|
||||
public static Guid? ReadValueAsGuid(this JsonReader reader)
|
||||
{
|
||||
Guid? value = null;
|
||||
|
||||
switch (reader.TokenType)
|
||||
{
|
||||
case JsonToken.String:
|
||||
value = ParseAsType((string)reader.Value, Guid.Parse);
|
||||
break;
|
||||
case JsonToken.Null:
|
||||
// value is initialized to null
|
||||
break;
|
||||
default:
|
||||
throw new JsonReaderException($"Error reading Date. Unexpected token: {reader.TokenType}.");
|
||||
}
|
||||
|
||||
reader.Read();
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Skips a property value.
|
||||
/// </summary>
|
||||
/// <param name="reader">Json reader.</param>
|
||||
public static void SkipPropertyValue(this JsonReader reader)
|
||||
{
|
||||
if (reader.TokenType.Equals(JsonToken.StartObject) || reader.TokenType.Equals(JsonToken.StartArray))
|
||||
{
|
||||
reader.Skip();
|
||||
}
|
||||
|
||||
reader.Read();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a json array.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of list elements.</typeparam>
|
||||
/// <param name="reader">The JsonReader object.</param>
|
||||
/// <param name="deserializerFunc">Func to deserialize T.</param>
|
||||
/// <returns>Returns the List of T.</returns>
|
||||
public static List<T> ReadList<T>(this JsonReader reader, Func<JsonReader, T> deserializerFunc)
|
||||
{
|
||||
// handle null.
|
||||
if (reader.TokenType == JsonToken.Null)
|
||||
{
|
||||
reader.Read();
|
||||
return null;
|
||||
}
|
||||
|
||||
var value = new List<T>();
|
||||
reader.ReadStartArray();
|
||||
|
||||
do
|
||||
{
|
||||
// handle empty array.
|
||||
if (reader.TokenType == JsonToken.EndArray)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
var item = deserializerFunc(reader);
|
||||
value.Add(item);
|
||||
}
|
||||
while (reader.TokenType != JsonToken.EndArray);
|
||||
|
||||
reader.ReadEndArray();
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a Dictionary from json.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of Dictionary values.</typeparam>
|
||||
/// <param name="reader">The JsonReader object.</param>
|
||||
/// <param name="deserializerFunc">Func to deserialize T.</param>
|
||||
/// <returns>Returns the dictionary.</returns>
|
||||
public static Dictionary<string, T> ReadDictionary<T>(this JsonReader reader, Func<JsonReader, T> deserializerFunc)
|
||||
{
|
||||
// handle null.
|
||||
if (reader.TokenType == JsonToken.Null)
|
||||
{
|
||||
reader.Read();
|
||||
return null;
|
||||
}
|
||||
|
||||
var dict = new Dictionary<string, T>();
|
||||
reader.ReadStartObject();
|
||||
|
||||
do
|
||||
{
|
||||
// handle empty dictionary.
|
||||
if (reader.TokenType == JsonToken.EndObject)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// key is propertyName, read property value and move to next token.
|
||||
var key = reader.ReadPropertyName();
|
||||
var value = deserializerFunc(reader);
|
||||
dict.Add(key, value);
|
||||
}
|
||||
while (reader.TokenType != JsonToken.EndObject);
|
||||
|
||||
reader.ReadEndObject();
|
||||
return dict;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deserializes Json representing of type T.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type to deserialize into.</typeparam>
|
||||
/// <param name="reader">Json Reader.</param>
|
||||
/// <param name="getFromJsonPropertiesFunc">Delegate to parse json properties for type T.</param>
|
||||
/// <returns>Deserialized object of type T. returns default(T) if Json Token represented by reader is null
|
||||
/// OR its an empty Json.</returns>
|
||||
public static T Deserialize<T>(this JsonReader reader, Func<JsonReader, T> getFromJsonPropertiesFunc)
|
||||
{
|
||||
var obj = default(T);
|
||||
|
||||
// handle null.
|
||||
if (reader.TokenType.Equals(JsonToken.Null))
|
||||
{
|
||||
reader.Read();
|
||||
return obj;
|
||||
}
|
||||
|
||||
// Handle JsonReader created over stream of length 0.
|
||||
reader.MoveToContent();
|
||||
if (reader.TokenType.Equals(JsonToken.None))
|
||||
{
|
||||
return obj;
|
||||
}
|
||||
|
||||
// handle Empty Json.
|
||||
reader.ReadStartObject();
|
||||
if (reader.TokenType.Equals(JsonToken.EndObject))
|
||||
{
|
||||
reader.ReadEndObject();
|
||||
return obj;
|
||||
}
|
||||
|
||||
// not empty json, get value by reading properties.
|
||||
obj = getFromJsonPropertiesFunc.Invoke(reader);
|
||||
reader.ReadEndObject();
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses type T from string value.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type T to parse value as.</typeparam>
|
||||
/// <param name="value">value to parse.</param>
|
||||
/// <param name="parseFunc">Parse Function.</param>
|
||||
/// <returns>Parsed value.</returns>
|
||||
private static T ParseAsType<T>(string value, Func<string, T> parseFunc)
|
||||
{
|
||||
T result;
|
||||
|
||||
try
|
||||
{
|
||||
result = parseFunc(value);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new JsonReaderException(
|
||||
$"Error converting string to {typeof(T)}, string value to be converted is {value}", ex);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses type T from string value.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type T to parse value as.</typeparam>
|
||||
/// <param name="value">value to parse.</param>
|
||||
/// <param name="parseFunc">Parse Function.</param>
|
||||
/// <returns>Parsed value.</returns>
|
||||
private static T ParseAsTypeWithInvariantCulture<T>(string value, Func<string, CultureInfo, T> parseFunc)
|
||||
{
|
||||
T result;
|
||||
|
||||
try
|
||||
{
|
||||
result = parseFunc(value, CultureInfo.InvariantCulture);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new JsonReaderException(
|
||||
$"Error converting string to {typeof(T)}, string value to be converted is {value}", ex);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,285 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Xml;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
/// <summary>
|
||||
/// Defines extension methods for JsonWriter.
|
||||
/// </summary>
|
||||
internal static class JsonWriterExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Writes a DateTime value in ISO8601 foramt.
|
||||
/// </summary>
|
||||
/// <param name="writer">Json writer.</param>
|
||||
/// <param name="value">Value to write.</param>
|
||||
public static void WriteDateTimeValueISO8601Format(this JsonWriter writer, DateTime? value)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
writer.WriteNull();
|
||||
}
|
||||
else
|
||||
{
|
||||
// write in ISO8601 foramt.
|
||||
writer.WriteValue(XmlConvert.ToString(value.Value, XmlDateTimeSerializationMode.Utc));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a TimeSpan value in ISO8601 foramt.
|
||||
/// </summary>
|
||||
/// <param name="writer">Json writer.</param>
|
||||
/// <param name="value">Value to write.</param>
|
||||
public static void WriteTimeSpanValueISO8601Format(this JsonWriter writer, TimeSpan? value)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
writer.WriteNull();
|
||||
}
|
||||
else
|
||||
{
|
||||
// write in ISO8601 foramt.
|
||||
writer.WriteValue(XmlConvert.ToString(value.Value));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a TimeSpan value in format expected by Actions.
|
||||
/// </summary>
|
||||
/// <param name="writer">Json writer.</param>
|
||||
/// <param name="value">Value to write.</param>
|
||||
public static void WriteTimeSpanValueActionsFormat(this JsonWriter writer, TimeSpan? value)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
writer.WriteNull();
|
||||
}
|
||||
else
|
||||
{
|
||||
// write in format expected by Actions, it only accepts h, m, s, ms, us(micro), ns
|
||||
var stringValue = string.Empty;
|
||||
if (value.Value >= TimeSpan.Zero)
|
||||
{
|
||||
var hours = (value.Value.Days * 24) + value.Value.Hours;
|
||||
stringValue = FormattableString.Invariant($"{hours}h{value.Value.Minutes}m{value.Value.Seconds}s{value.Value.Milliseconds}ms");
|
||||
}
|
||||
|
||||
writer.WriteValue(stringValue);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a string value.
|
||||
/// </summary>
|
||||
/// <param name="writer">Json writer.</param>
|
||||
/// <param name="value">Value to write.</param>
|
||||
public static void WriteStringValue(this JsonWriter writer, string value)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
writer.WriteNull();
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.WriteValue(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes integer value.
|
||||
/// </summary>
|
||||
/// <param name="writer">Json writer.</param>
|
||||
/// <param name="value">Value to write.</param>
|
||||
public static void WriteIntValue(this JsonWriter writer, int? value)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
writer.WriteNull();
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.WriteValue(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes long value.
|
||||
/// </summary>
|
||||
/// <param name="writer">Json writer.</param>
|
||||
/// <param name="value">Value to write.</param>
|
||||
public static void WriteLongValue(this JsonWriter writer, long? value)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
writer.WriteNull();
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.WriteValue(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes double value.
|
||||
/// </summary>
|
||||
/// <param name="writer">Json writer.</param>
|
||||
/// <param name="value">Value to write.</param>
|
||||
public static void WriteDoubleValue(this JsonWriter writer, double? value)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
writer.WriteNull();
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.WriteValue(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes boolean value.
|
||||
/// </summary>
|
||||
/// <param name="writer">Json writer.</param>
|
||||
/// <param name="value">Value to write.</param>
|
||||
public static void WriteBoolValue(this JsonWriter writer, bool? value)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
writer.WriteNull();
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.WriteValue(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes Guid value.
|
||||
/// </summary>
|
||||
/// <param name="writer">Json writer.</param>
|
||||
/// <param name="value">Value to write.</param>
|
||||
public static void WriteGuidValue(this JsonWriter writer, Guid? value)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
writer.WriteNull();
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.WriteValue(value.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes byte value.
|
||||
/// </summary>
|
||||
/// <param name="writer">Json writer.</param>
|
||||
/// <param name="value">Value to write.</param>
|
||||
public static void WriteByteValue(this JsonWriter writer, byte value)
|
||||
{
|
||||
// byte is int in json.
|
||||
writer.WriteValue(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes property.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of property to write.</typeparam>
|
||||
/// <param name="writer">JsonWriter instance.</param>
|
||||
/// <param name="obj">object of type T to write.</param>
|
||||
/// <param name="propertyName">Property NameDescription to write as.</param>
|
||||
/// <param name="serializeFunc">Func to serialize obj of type T.</param>
|
||||
public static void WriteProperty<T>(this JsonWriter writer, T obj, string propertyName, Action<JsonWriter, T> serializeFunc)
|
||||
{
|
||||
writer.WritePropertyName(propertyName);
|
||||
if (obj == null)
|
||||
{
|
||||
writer.WriteNull();
|
||||
}
|
||||
else
|
||||
{
|
||||
serializeFunc.Invoke(writer, obj);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes IEnumerable property as json array.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of IEnumerable elements.</typeparam>
|
||||
/// <param name="writer">JsonWriter instance.</param>
|
||||
/// <param name="sequence">IEnumerable to write.</param>
|
||||
/// <param name="propertyName">Property NameDescription to write as.</param>
|
||||
/// <param name="serializeFunc">Func to serialize obj of type T.</param>
|
||||
public static void WriteEnumerableProperty<T>(this JsonWriter writer, IEnumerable<T> sequence, string propertyName, Action<JsonWriter, T> serializeFunc)
|
||||
{
|
||||
writer.WritePropertyName(propertyName);
|
||||
if (sequence == null)
|
||||
{
|
||||
writer.WriteNull();
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.WriteStartArray();
|
||||
|
||||
foreach (var item in sequence)
|
||||
{
|
||||
if (item == null)
|
||||
{
|
||||
writer.WriteNull();
|
||||
}
|
||||
else
|
||||
{
|
||||
serializeFunc.Invoke(writer, item);
|
||||
}
|
||||
}
|
||||
|
||||
writer.WriteEndArray();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes properties of IDictionary.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of dictionary values.</typeparam>
|
||||
/// <param name="writer">JsonWriter instance.</param>
|
||||
/// <param name="collection">Dictionary object to write.</param>
|
||||
/// <param name="propertyName">Property NameDescription to write as.</param>
|
||||
/// <param name="serializeFunc">Func to serialize obj of type T.</param>
|
||||
public static void WriteDictionaryProperty<T>(this JsonWriter writer, IReadOnlyDictionary<string, T> collection, string propertyName, Action<JsonWriter, T> serializeFunc)
|
||||
{
|
||||
writer.WritePropertyName(propertyName);
|
||||
if (collection == null)
|
||||
{
|
||||
writer.WriteNull();
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.WriteStartObject();
|
||||
|
||||
foreach (var item in collection)
|
||||
{
|
||||
writer.WritePropertyName(item.Key);
|
||||
if (item.Value == null)
|
||||
{
|
||||
writer.WriteNull();
|
||||
}
|
||||
else
|
||||
{
|
||||
serializeFunc.Invoke(writer, item.Value);
|
||||
}
|
||||
}
|
||||
|
||||
writer.WriteEndObject();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -12,7 +12,10 @@
|
|||
<NuspecProperties>version=$(NupkgVersion);Configuration=$(Configuration)</NuspecProperties>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.CSharp" Version="4.5.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
|
||||
<PackageReference Include="System.Reflection.Emit" Version="4.3.0" />
|
||||
<PackageReference Include="System.Reflection.Emit.ILGeneration" Version="4.3.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Update="StyleCop.Analyzers" Version="1.1.118">
|
||||
|
|
|
|||
|
|
@ -1,81 +1,396 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors.Resources {
|
||||
using System;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class SR {
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal SR() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||
get {
|
||||
if (object.ReferenceEquals(resourceMan, null)) {
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.Actions.Actors.Resources.SR", typeof(SR).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
set {
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to The requested resource/content/path does not exist on the server..
|
||||
/// </summary>
|
||||
internal static string ErrorMessageHTTP404 {
|
||||
get {
|
||||
return ResourceManager.GetString("ErrorMessageHTTP404", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Server returned error while processing the request, but did not provide a meaningful error response. Response Error Code {0}.
|
||||
/// </summary>
|
||||
internal static string ServerErrorNoMeaningFulResponse {
|
||||
get {
|
||||
return ResourceManager.GetString("ServerErrorNoMeaningFulResponse", resourceCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors.Resources {
|
||||
using System;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class SR {
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal SR() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||
get {
|
||||
if (object.ReferenceEquals(resourceMan, null)) {
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.Actions.Actors.Resources.SR", typeof(SR).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
set {
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to The actor state name '{0}' already exist..
|
||||
/// </summary>
|
||||
internal static string ActorStateAlreadyExists {
|
||||
get {
|
||||
return ResourceManager.GetString("ActorStateAlreadyExists", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Invalid Client for remoting..
|
||||
/// </summary>
|
||||
internal static string Error_InvalidOperation {
|
||||
get {
|
||||
return ResourceManager.GetString("Error_InvalidOperation", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to CallBack Channel Not Found for this ClientId : '{0}'.
|
||||
/// </summary>
|
||||
internal static string ErrorClientCallbackChannelNotFound {
|
||||
get {
|
||||
return ResourceManager.GetString("ErrorClientCallbackChannelNotFound", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Failed to deserialize and get remote exception {0}.
|
||||
/// </summary>
|
||||
internal static string ErrorDeserializationFailure {
|
||||
get {
|
||||
return ResourceManager.GetString("ErrorDeserializationFailure", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to The type '{0} is not an actor events interface. The actor event interface must only derive from '{1}'..
|
||||
/// </summary>
|
||||
internal static string ErrorEventInterfaceMustBeIActorEvents {
|
||||
get {
|
||||
return ResourceManager.GetString("ErrorEventInterfaceMustBeIActorEvents", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to The exception {0} was unhandled on the service and could not be serialized for transferring to the client..
|
||||
/// </summary>
|
||||
internal static string ErrorExceptionSerializationFailed1 {
|
||||
get {
|
||||
return ResourceManager.GetString("ErrorExceptionSerializationFailed1", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Detailed Remote Exception Information: {0}.
|
||||
/// </summary>
|
||||
internal static string ErrorExceptionSerializationFailed2 {
|
||||
get {
|
||||
return ResourceManager.GetString("ErrorExceptionSerializationFailed2", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Header with name '{0}' already exists.
|
||||
/// </summary>
|
||||
internal static string ErrorHeaderAlreadyExists {
|
||||
get {
|
||||
return ResourceManager.GetString("ErrorHeaderAlreadyExists", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Interface id '{0}' is not implemented by object '{1}'.
|
||||
/// </summary>
|
||||
internal static string ErrorInterfaceNotImplemented {
|
||||
get {
|
||||
return ResourceManager.GetString("ErrorInterfaceNotImplemented", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Client is trying to connect to invalid address {0}..
|
||||
/// </summary>
|
||||
internal static string ErrorInvalidAddress {
|
||||
get {
|
||||
return ResourceManager.GetString("ErrorInvalidAddress", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to The requested resource/content/path does not exist on the server..
|
||||
/// </summary>
|
||||
internal static string ErrorMessageHTTP404 {
|
||||
get {
|
||||
return ResourceManager.GetString("ErrorMessageHTTP404", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to No MethodDispatcher is found for interface id '{0}'.
|
||||
/// </summary>
|
||||
internal static string ErrorMethodDispatcherNotFound {
|
||||
get {
|
||||
return ResourceManager.GetString("ErrorMethodDispatcherNotFound", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to The object of type '{0}' does support the method '{1}'.
|
||||
/// </summary>
|
||||
internal static string ErrorMethodNotImplemented {
|
||||
get {
|
||||
return ResourceManager.GetString("ErrorMethodNotImplemented", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Method '{0}' of interface '{1}' is not supported in remoting V1..
|
||||
/// </summary>
|
||||
internal static string ErrorMethodNotSupportedInRemotingV1 {
|
||||
get {
|
||||
return ResourceManager.GetString("ErrorMethodNotSupportedInRemotingV1", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Method Id '{0}' for interface Id '{1}' not found in service implementation. If a new method is added to interface and client & service are being upgraded at the same time, its possible that client got upgraded before the service. If a method is removed from the interface and client & service are being upgraded at the same time, its possible that service got upgraded before the client. Addition or removal of methods to an interface should be performed as a phased upgrade..
|
||||
/// </summary>
|
||||
internal static string ErrorMissingMethod {
|
||||
get {
|
||||
return ResourceManager.GetString("ErrorMissingMethod", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Actor State with name {0} was not found..
|
||||
/// </summary>
|
||||
internal static string ErrorNamedActorStateNotFound {
|
||||
get {
|
||||
return ResourceManager.GetString("ErrorNamedActorStateNotFound", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to The actor type '{0}' does not implement any actor interfaces or one of the interfaces implemented is not an actor interface. All interfaces(including its parent interface) implemented by actor type must be actor interface. An actor interface is the one that ultimately derives from '{1}' type..
|
||||
/// </summary>
|
||||
internal static string ErrorNoActorInterfaceFound {
|
||||
get {
|
||||
return ResourceManager.GetString("ErrorNoActorInterfaceFound", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to The service type '{0}' does not implement any service interfaces or one of the interfaces implemented is not a service interface. All interfaces(including its parent interface) implemented by service type must be service interface. A service interface is the one that ultimately derives from '{1}' type..
|
||||
/// </summary>
|
||||
internal static string ErrorNoServiceInterfaceFound {
|
||||
get {
|
||||
return ResourceManager.GetString("ErrorNoServiceInterfaceFound", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to The type '{0}' is not an Actor. An actor type must derive from '{1}'..
|
||||
/// </summary>
|
||||
internal static string ErrorNotAnActor {
|
||||
get {
|
||||
return ResourceManager.GetString("ErrorNotAnActor", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to The type '{0}' is not an actor interface as it does not derive from the interface '{1}'..
|
||||
/// </summary>
|
||||
internal static string ErrorNotAnActorInterface_DerivationCheck1 {
|
||||
get {
|
||||
return ResourceManager.GetString("ErrorNotAnActorInterface_DerivationCheck1", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to The type '{0}' is not an actor interface as it derive from a non actor interface '{1}'. All actor interfaces must derive from '{2}'..
|
||||
/// </summary>
|
||||
internal static string ErrorNotAnActorInterface_DerivationCheck2 {
|
||||
get {
|
||||
return ResourceManager.GetString("ErrorNotAnActorInterface_DerivationCheck2", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to The type '{0}' is not an Actor interface as it is not an interface..
|
||||
/// </summary>
|
||||
internal static string ErrorNotAnActorInterface_InterfaceCheck {
|
||||
get {
|
||||
return ResourceManager.GetString("ErrorNotAnActorInterface_InterfaceCheck", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to The type '{0}' is not an service interface as it does not derive from the interface '{1}'..
|
||||
/// </summary>
|
||||
internal static string ErrorNotAServiceInterface_DerivationCheck1 {
|
||||
get {
|
||||
return ResourceManager.GetString("ErrorNotAServiceInterface_DerivationCheck1", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to The type '{0}' is not an service interface as it derive from a non service interface '{1}'. All service interfaces must derive from '{2}'..
|
||||
/// </summary>
|
||||
internal static string ErrorNotAServiceInterface_DerivationCheck2 {
|
||||
get {
|
||||
return ResourceManager.GetString("ErrorNotAServiceInterface_DerivationCheck2", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to The type '{0}' is not a service interface as it is not an interface. .
|
||||
/// </summary>
|
||||
internal static string ErrorNotAServiceInterface_InterfaceCheck {
|
||||
get {
|
||||
return ResourceManager.GetString("ErrorNotAServiceInterface_InterfaceCheck", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to The {0} interface '{1}' is using generics. Generic interfaces cannot be remoted..
|
||||
/// </summary>
|
||||
internal static string ErrorRemotedInterfaceIsGeneric {
|
||||
get {
|
||||
return ResourceManager.GetString("ErrorRemotedInterfaceIsGeneric", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Method '{1}' of {0} interface '{2}' has '{4}' parameter '{3}', and it is not the last parameter. If a method of the {0} interface has parameter of type '{4}' it must be the last parameter..
|
||||
/// </summary>
|
||||
internal static string ErrorRemotedMethodCancellationTokenOutOfOrder {
|
||||
get {
|
||||
return ResourceManager.GetString("ErrorRemotedMethodCancellationTokenOutOfOrder", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Method '{1}' of {0} interface '{2}' does not return Task or Task<>. The {0} interface methods must be async and must return either Task or Task<>..
|
||||
/// </summary>
|
||||
internal static string ErrorRemotedMethodDoesNotReturnTask {
|
||||
get {
|
||||
return ResourceManager.GetString("ErrorRemotedMethodDoesNotReturnTask", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Method '{1}' of {0} interface '{2}' returns '{3}'. The {0} interface methods must have a return of type '{4}'..
|
||||
/// </summary>
|
||||
internal static string ErrorRemotedMethodDoesNotReturnVoid {
|
||||
get {
|
||||
return ResourceManager.GetString("ErrorRemotedMethodDoesNotReturnVoid", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Method '{1}' of {0} interface '{2}' is using generics. The {0} interface methods cannot use generics..
|
||||
/// </summary>
|
||||
internal static string ErrorRemotedMethodHasGenerics {
|
||||
get {
|
||||
return ResourceManager.GetString("ErrorRemotedMethodHasGenerics", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Method '{1}' of {0} interface '{2}' has out/ref/optional parameter '{3}'. The {0} interface methods must not have out, ref or optional parameters..
|
||||
/// </summary>
|
||||
internal static string ErrorRemotedMethodHasOutRefOptionalParameter {
|
||||
get {
|
||||
return ResourceManager.GetString("ErrorRemotedMethodHasOutRefOptionalParameter", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Method '{1}' of {0} interface '{2}' has variable length parameter '{3}'. The {0} interface methods must not have variable length parameters..
|
||||
/// </summary>
|
||||
internal static string ErrorRemotedMethodHasVaArgParameter {
|
||||
get {
|
||||
return ResourceManager.GetString("ErrorRemotedMethodHasVaArgParameter", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Method '{1}' of {0} interface '{2}' is using a variable argument list. The {0} interface methods cannot have a variable argument list..
|
||||
/// </summary>
|
||||
internal static string ErrorRemotedMethodHasVaArgs {
|
||||
get {
|
||||
return ResourceManager.GetString("ErrorRemotedMethodHasVaArgs", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Method '{1}' of {0} interface '{2}' is overloaded. The {0} interface methods cannot be overloaded..
|
||||
/// </summary>
|
||||
internal static string ErrorRemotedMethodsIsOverloaded {
|
||||
get {
|
||||
return ResourceManager.GetString("ErrorRemotedMethodsIsOverloaded", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to The method '{0}' is not valid for '{1}' ActorId..
|
||||
/// </summary>
|
||||
internal static string InvalidActorKind {
|
||||
get {
|
||||
return ResourceManager.GetString("InvalidActorKind", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Server returned error while processing the request, but did not provide a meaningful error response. Response Error Code {0}.
|
||||
/// </summary>
|
||||
internal static string ServerErrorNoMeaningFulResponse {
|
||||
get {
|
||||
return ResourceManager.GetString("ServerErrorNoMeaningFulResponse", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to TimeSpan TotalMilliseconds specified value must be between {0} and {1} .
|
||||
/// </summary>
|
||||
internal static string TimerArgumentOutOfRange {
|
||||
get {
|
||||
return ResourceManager.GetString("TimerArgumentOutOfRange", resourceCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,126 +1,231 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="ErrorMessageHTTP404" xml:space="preserve">
|
||||
<value>The requested resource/content/path does not exist on the server.</value>
|
||||
</data>
|
||||
<data name="ServerErrorNoMeaningFulResponse" xml:space="preserve">
|
||||
<value>Server returned error while processing the request, but did not provide a meaningful error response. Response Error Code {0}</value>
|
||||
</data>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="ActorStateAlreadyExists" xml:space="preserve">
|
||||
<value>The actor state name '{0}' already exist.</value>
|
||||
</data>
|
||||
<data name="ErrorClientCallbackChannelNotFound" xml:space="preserve">
|
||||
<value>CallBack Channel Not Found for this ClientId : '{0}'</value>
|
||||
</data>
|
||||
<data name="ErrorDeserializationFailure" xml:space="preserve">
|
||||
<value>Failed to deserialize and get remote exception {0}</value>
|
||||
</data>
|
||||
<data name="ErrorEventInterfaceMustBeIActorEvents" xml:space="preserve">
|
||||
<value>The type '{0} is not an actor events interface. The actor event interface must only derive from '{1}'.</value>
|
||||
</data>
|
||||
<data name="ErrorExceptionSerializationFailed1" xml:space="preserve">
|
||||
<value>The exception {0} was unhandled on the service and could not be serialized for transferring to the client.</value>
|
||||
</data>
|
||||
<data name="ErrorExceptionSerializationFailed2" xml:space="preserve">
|
||||
<value>Detailed Remote Exception Information: {0}</value>
|
||||
</data>
|
||||
<data name="ErrorHeaderAlreadyExists" xml:space="preserve">
|
||||
<value>Header with name '{0}' already exists</value>
|
||||
</data>
|
||||
<data name="ErrorInterfaceNotImplemented" xml:space="preserve">
|
||||
<value>Interface id '{0}' is not implemented by object '{1}'</value>
|
||||
</data>
|
||||
<data name="ErrorInvalidAddress" xml:space="preserve">
|
||||
<value>Client is trying to connect to invalid address {0}.</value>
|
||||
</data>
|
||||
<data name="ErrorMessageHTTP404" xml:space="preserve">
|
||||
<value>The requested resource/content/path does not exist on the server.</value>
|
||||
</data>
|
||||
<data name="ErrorMethodNotImplemented" xml:space="preserve">
|
||||
<value>The object of type '{0}' does support the method '{1}'</value>
|
||||
</data>
|
||||
<data name="ErrorMethodNotSupportedInRemotingV1" xml:space="preserve">
|
||||
<value>Method '{0}' of interface '{1}' is not supported in remoting V1.</value>
|
||||
</data>
|
||||
<data name="ErrorMissingMethod" xml:space="preserve">
|
||||
<value>Method Id '{0}' for interface Id '{1}' not found in service implementation. If a new method is added to interface and client & service are being upgraded at the same time, its possible that client got upgraded before the service. If a method is removed from the interface and client & service are being upgraded at the same time, its possible that service got upgraded before the client. Addition or removal of methods to an interface should be performed as a phased upgrade.</value>
|
||||
</data>
|
||||
<data name="ErrorNamedActorStateNotFound" xml:space="preserve">
|
||||
<value>Actor State with name {0} was not found.</value>
|
||||
</data>
|
||||
<data name="ErrorNoServiceInterfaceFound" xml:space="preserve">
|
||||
<value>The service type '{0}' does not implement any service interfaces or one of the interfaces implemented is not a service interface. All interfaces(including its parent interface) implemented by service type must be service interface. A service interface is the one that ultimately derives from '{1}' type.</value>
|
||||
</data>
|
||||
<data name="ErrorNotAnActorInterface_DerivationCheck1" xml:space="preserve">
|
||||
<value>The type '{0}' is not an actor interface as it does not derive from the interface '{1}'.</value>
|
||||
</data>
|
||||
<data name="ErrorNotAnActorInterface_DerivationCheck2" xml:space="preserve">
|
||||
<value>The type '{0}' is not an actor interface as it derive from a non actor interface '{1}'. All actor interfaces must derive from '{2}'.</value>
|
||||
</data>
|
||||
<data name="ErrorNotAnActorInterface_InterfaceCheck" xml:space="preserve">
|
||||
<value>The type '{0}' is not an Actor interface as it is not an interface.</value>
|
||||
</data>
|
||||
<data name="ErrorNotAServiceInterface_DerivationCheck1" xml:space="preserve">
|
||||
<value>The type '{0}' is not an service interface as it does not derive from the interface '{1}'.</value>
|
||||
</data>
|
||||
<data name="ErrorNotAServiceInterface_DerivationCheck2" xml:space="preserve">
|
||||
<value>The type '{0}' is not an service interface as it derive from a non service interface '{1}'. All service interfaces must derive from '{2}'.</value>
|
||||
</data>
|
||||
<data name="ErrorNotAServiceInterface_InterfaceCheck" xml:space="preserve">
|
||||
<value>The type '{0}' is not a service interface as it is not an interface. </value>
|
||||
</data>
|
||||
<data name="ErrorRemotedInterfaceIsGeneric" xml:space="preserve">
|
||||
<value>The {0} interface '{1}' is using generics. Generic interfaces cannot be remoted.</value>
|
||||
</data>
|
||||
<data name="ErrorRemotedMethodCancellationTokenOutOfOrder" xml:space="preserve">
|
||||
<value>Method '{1}' of {0} interface '{2}' has '{4}' parameter '{3}', and it is not the last parameter. If a method of the {0} interface has parameter of type '{4}' it must be the last parameter.</value>
|
||||
</data>
|
||||
<data name="ErrorRemotedMethodDoesNotReturnTask" xml:space="preserve">
|
||||
<value>Method '{1}' of {0} interface '{2}' does not return Task or Task<>. The {0} interface methods must be async and must return either Task or Task<>.</value>
|
||||
</data>
|
||||
<data name="ErrorRemotedMethodDoesNotReturnVoid" xml:space="preserve">
|
||||
<value>Method '{1}' of {0} interface '{2}' returns '{3}'. The {0} interface methods must have a return of type '{4}'.</value>
|
||||
</data>
|
||||
<data name="ErrorRemotedMethodHasGenerics" xml:space="preserve">
|
||||
<value>Method '{1}' of {0} interface '{2}' is using generics. The {0} interface methods cannot use generics.</value>
|
||||
</data>
|
||||
<data name="ErrorRemotedMethodHasOutRefOptionalParameter" xml:space="preserve">
|
||||
<value>Method '{1}' of {0} interface '{2}' has out/ref/optional parameter '{3}'. The {0} interface methods must not have out, ref or optional parameters.</value>
|
||||
</data>
|
||||
<data name="ErrorRemotedMethodHasVaArgParameter" xml:space="preserve">
|
||||
<value>Method '{1}' of {0} interface '{2}' has variable length parameter '{3}'. The {0} interface methods must not have variable length parameters.</value>
|
||||
</data>
|
||||
<data name="ErrorRemotedMethodHasVaArgs" xml:space="preserve">
|
||||
<value>Method '{1}' of {0} interface '{2}' is using a variable argument list. The {0} interface methods cannot have a variable argument list.</value>
|
||||
</data>
|
||||
<data name="ErrorRemotedMethodsIsOverloaded" xml:space="preserve">
|
||||
<value>Method '{1}' of {0} interface '{2}' is overloaded. The {0} interface methods cannot be overloaded.</value>
|
||||
</data>
|
||||
<data name="Error_InvalidOperation" xml:space="preserve">
|
||||
<value>Invalid Client for remoting.</value>
|
||||
</data>
|
||||
<data name="InvalidActorKind" xml:space="preserve">
|
||||
<value>The method '{0}' is not valid for '{1}' ActorId.</value>
|
||||
</data>
|
||||
<data name="ServerErrorNoMeaningFulResponse" xml:space="preserve">
|
||||
<value>Server returned error while processing the request, but did not provide a meaningful error response. Response Error Code {0}</value>
|
||||
</data>
|
||||
<data name="ErrorMethodDispatcherNotFound" xml:space="preserve">
|
||||
<value>No MethodDispatcher is found for interface id '{0}'</value>
|
||||
</data>
|
||||
<data name="ErrorNoActorInterfaceFound" xml:space="preserve">
|
||||
<value>The actor type '{0}' does not implement any actor interfaces or one of the interfaces implemented is not an actor interface. All interfaces(including its parent interface) implemented by actor type must be actor interface. An actor interface is the one that ultimately derives from '{1}' type.</value>
|
||||
</data>
|
||||
<data name="ErrorNotAnActor" xml:space="preserve">
|
||||
<value>The type '{0}' is not an Actor. An actor type must derive from '{1}'.</value>
|
||||
</data>
|
||||
<data name="TimerArgumentOutOfRange" xml:space="preserve">
|
||||
<value>TimeSpan TotalMilliseconds specified value must be between {0} and {1} </value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors.Runtime
|
||||
{
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
internal class ActionsActorStateManager : IActorStateManager
|
||||
{
|
||||
public Task AddOrUpdateState<T>(string stateName, T value)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public Task ClearCacheAsync(CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<T> GetStateAsync<T>(string stateName, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public Task RemoveStateAsync(string stateName, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public Task SaveStateAsync(CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5,31 +5,160 @@
|
|||
|
||||
namespace Microsoft.Actions.Actors.Runtime
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
/// <summary>
|
||||
/// State Provider to interact with Actions runtime.
|
||||
/// </summary>
|
||||
public class ActionsStateProvider : IActorStateProvider
|
||||
internal class ActionsStateProvider
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public Task<T> LoadStateAsync<T>(ActorId actorId, string stateName, CancellationToken cancellationToken = default(CancellationToken))
|
||||
private readonly ActorStateProviderSerializer actorStateSerializer;
|
||||
|
||||
public ActionsStateProvider(ActorStateProviderSerializer actorStateSerializer)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
this.actorStateSerializer = actorStateSerializer;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task RemoveActorAsync(ActorId actorId, CancellationToken cancellationToken = default(CancellationToken))
|
||||
public async Task<ConditionalValue<T>> TryLoadStateAsync<T>(string actorType, string actorId, string stateName, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
var result = new ConditionalValue<T>(false, default(T));
|
||||
var byteResult = await ActorRuntime.ActionsInteractor.GetStateAsync(actorType, actorId, stateName);
|
||||
|
||||
if (byteResult.Length != 0)
|
||||
{
|
||||
var typedResult = this.actorStateSerializer.Deserialize<T>(byteResult);
|
||||
result = new ConditionalValue<T>(true, typedResult);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task SaveStateAsync(ActorId actorId, IReadOnlyCollection<ActorStateChange> stateChanges, CancellationToken cancellationToken = default(CancellationToken))
|
||||
public async Task<bool> ContainsStateAsync(string actorType, string actorId, string stateName, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
var byteResult = await ActorRuntime.ActionsInteractor.GetStateAsync(actorType, actorId, stateName);
|
||||
return byteResult.Length != 0;
|
||||
}
|
||||
|
||||
public async Task SaveStateAsync(string actorType, string actorId, IReadOnlyCollection<ActorStateChange> stateChanges, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
await this.DoStateChangesTransactionallyAsync(actorType, actorId, stateChanges, cancellationToken);
|
||||
}
|
||||
|
||||
private Task DoStateChangesTransactionallyAsync(string actorType, string actorId, IReadOnlyCollection<ActorStateChange> stateChanges, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
// Transactional state update request body:
|
||||
/*
|
||||
[
|
||||
{
|
||||
"operation": "upsert",
|
||||
"request": {
|
||||
"key": "key1",
|
||||
"value": "myData"
|
||||
}
|
||||
},
|
||||
{
|
||||
"operation": "delete",
|
||||
"request": {
|
||||
"key": "key2"
|
||||
}
|
||||
}
|
||||
]
|
||||
*/
|
||||
|
||||
string content;
|
||||
using (var sw = new StringWriter())
|
||||
{
|
||||
var writer = new JsonTextWriter(sw);
|
||||
writer.WriteStartArray();
|
||||
|
||||
foreach (var stateChange in stateChanges)
|
||||
{
|
||||
writer.WriteStartObject();
|
||||
var operation = this.GetActionsStateOperation(stateChange.ChangeKind);
|
||||
writer.WriteProperty(operation, "operation", JsonWriterExtensions.WriteStringValue);
|
||||
writer.WriteProperty(stateChange, "request", this.SerializeStateChangeRequest);
|
||||
writer.WriteEndObject();
|
||||
}
|
||||
|
||||
writer.WriteEndArray();
|
||||
content = sw.ToString();
|
||||
}
|
||||
|
||||
return ActorRuntime.ActionsInteractor.SaveStateTransactionallyAsync(actorType, actorId, content, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Save state individually. Actions runtime has added Tranaction save state. Use that instead. This method exists for debugging and testing of save state individually.
|
||||
/// </summary>
|
||||
private async Task DoStateChangesIndividuallyAsync(string actorType, string actorId, IReadOnlyCollection<ActorStateChange> stateChanges, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
foreach (var stateChange in stateChanges)
|
||||
{
|
||||
var keyName = stateChange.StateName;
|
||||
|
||||
switch (stateChange.ChangeKind)
|
||||
{
|
||||
case StateChangeKind.Remove:
|
||||
await ActorRuntime.ActionsInteractor.RemoveStateAsync(actorType, actorId, keyName, cancellationToken);
|
||||
break;
|
||||
case StateChangeKind.Add:
|
||||
case StateChangeKind.Update:
|
||||
// Currently Actions runtime only support json serialization.
|
||||
await ActorRuntime.ActionsInteractor.SaveStateAsync(actorType, actorId, keyName, JsonConvert.SerializeObject(stateChange.Value), cancellationToken);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string GetActionsStateOperation(StateChangeKind changeKind)
|
||||
{
|
||||
var operation = string.Empty;
|
||||
|
||||
switch (changeKind)
|
||||
{
|
||||
case StateChangeKind.Remove:
|
||||
operation = "delete";
|
||||
break;
|
||||
case StateChangeKind.Add:
|
||||
case StateChangeKind.Update:
|
||||
operation = "upsert";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return operation;
|
||||
}
|
||||
|
||||
private void SerializeStateChangeRequest(JsonWriter writer, ActorStateChange stateChange)
|
||||
{
|
||||
writer.WriteStartObject();
|
||||
|
||||
switch (stateChange.ChangeKind)
|
||||
{
|
||||
case StateChangeKind.Remove:
|
||||
writer.WriteProperty(stateChange.StateName, "key", JsonWriterExtensions.WriteStringValue);
|
||||
break;
|
||||
case StateChangeKind.Add:
|
||||
case StateChangeKind.Update:
|
||||
writer.WriteProperty(stateChange.StateName, "key", JsonWriterExtensions.WriteStringValue);
|
||||
var buffer = this.actorStateSerializer.Serialize(stateChange.Type, stateChange.Value);
|
||||
|
||||
writer.WriteProperty(Convert.ToBase64String(buffer), "value", JsonWriterExtensions.WriteStringValue);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
writer.WriteEndObject();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@
|
|||
|
||||
namespace Microsoft.Actions.Actors.Runtime
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -14,46 +16,68 @@ namespace Microsoft.Actions.Actors.Runtime
|
|||
/// The base type for actors, that provides the common functionality
|
||||
/// for actors that derive from <see cref="Actor"/>.
|
||||
/// The state is preserved across actor garbage collections and fail-overs.
|
||||
/// The storage and retrieval of the state is provided by the actor state provider. See
|
||||
/// <see cref="IActorStateProvider"/> for more information.
|
||||
/// </remarks>
|
||||
public abstract class Actor
|
||||
{
|
||||
private const string TraceType = "Actor";
|
||||
private readonly string traceId;
|
||||
private IActorStateManager stateManager;
|
||||
private readonly string actorImplementaionTypeName;
|
||||
|
||||
internal Actor(ActorId actorId)
|
||||
/// <summary>
|
||||
/// Contains timers to be invoked.
|
||||
/// </summary>
|
||||
private readonly Dictionary<string, IActorTimer> timers = new Dictionary<string, IActorTimer>();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Actor"/> class.
|
||||
/// </summary>
|
||||
/// <param name="actorService">The <see cref="ActorService"/> that will host this actor instance.</param>
|
||||
/// <param name="actorId">Id for the actor.</param>
|
||||
protected Actor(ActorService actorService, ActorId actorId)
|
||||
{
|
||||
this.Id = actorId;
|
||||
this.traceId = this.Id.GetStorageKey();
|
||||
this.traceId = this.Id.ToString();
|
||||
this.IsDirty = false;
|
||||
this.stateManager = new ActionsActorStateManager();
|
||||
this.IsInitialized = false;
|
||||
this.ActorService = actorService;
|
||||
this.StateManager = new ActorStateManager(this);
|
||||
this.actorImplementaionTypeName = this.ActorService.ActorTypeInfo.ImplementationType.Name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the identity of this actor with the actor service.
|
||||
/// Gets the identity of this actor.
|
||||
/// </summary>
|
||||
/// <value>The <see cref="ActorId"/> for the actor.</value>
|
||||
public ActorId Id { get; }
|
||||
public ActorId Id { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the host ActorService of this actor within the Actor runtime.
|
||||
/// </summary>
|
||||
/// <value>The <see cref="ActorService"/> for the actor.</value>
|
||||
public ActorService ActorService { get; }
|
||||
|
||||
internal ActorTrace TraceSource => ActorTrace.Instance;
|
||||
|
||||
internal bool IsDirty { get; set; }
|
||||
internal bool IsDirty { get; private set; }
|
||||
|
||||
internal bool IsInitialized { get; set; }
|
||||
/// <summary>
|
||||
/// Gets the StateManager for the actor.
|
||||
/// </summary>
|
||||
protected IActorStateManager StateManager { get; }
|
||||
|
||||
internal async Task OnActivateInternalAsync()
|
||||
{
|
||||
await this.ResetStateAsync();
|
||||
await this.OnActivateAsync();
|
||||
this.TraceSource.WriteInfoWithId(TraceType, this.traceId, "Activated");
|
||||
|
||||
// Save any state modifications done in user overridden Activate method.
|
||||
await this.SaveStateAsync();
|
||||
}
|
||||
|
||||
internal virtual async Task OnDeactivateInternalAsync()
|
||||
internal async Task OnDeactivateInternalAsync()
|
||||
{
|
||||
this.TraceSource.WriteInfoWithId(TraceType, this.traceId, "Deactivating ...");
|
||||
await this.stateManager.ClearCacheAsync();
|
||||
await this.ResetStateAsync();
|
||||
await this.OnDeactivateAsync();
|
||||
this.TraceSource.WriteInfoWithId(TraceType, this.traceId, "Deactivated");
|
||||
}
|
||||
|
|
@ -63,33 +87,26 @@ namespace Microsoft.Actions.Actors.Runtime
|
|||
return this.OnPreActorMethodAsync(actorMethodContext);
|
||||
}
|
||||
|
||||
internal Task OnPostActorMethodAsyncInternal(ActorMethodContext actorMethodContext)
|
||||
internal async Task OnPostActorMethodAsyncInternal(ActorMethodContext actorMethodContext)
|
||||
{
|
||||
return this.OnPostActorMethodAsync(actorMethodContext);
|
||||
await this.OnPostActorMethodAsync(actorMethodContext);
|
||||
await this.SaveStateAsync();
|
||||
}
|
||||
|
||||
internal void OnInvokeFailedInternal()
|
||||
internal void OnInvokeFailed()
|
||||
{
|
||||
this.IsDirty = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called from ActorManager to save state implicitly.
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous save operation.</returns>
|
||||
internal Task SaveStateAsyncInternal()
|
||||
{
|
||||
return this.SaveStateAsync();
|
||||
}
|
||||
|
||||
internal Task ResetStateAsync()
|
||||
{
|
||||
return this.stateManager.ClearCacheAsync();
|
||||
return this.StateManager.ClearCacheAsync();
|
||||
}
|
||||
|
||||
internal Task OnPostActivateAsync()
|
||||
internal Task FireTimerAsync(string timerName)
|
||||
{
|
||||
return this.SaveStateAsync();
|
||||
var timer = this.timers[timerName];
|
||||
return timer.AsyncCallback.Invoke(timer.State);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -102,7 +119,7 @@ namespace Microsoft.Actions.Actors.Runtime
|
|||
{
|
||||
if (!this.IsDirty)
|
||||
{
|
||||
await this.stateManager.SaveStateAsync();
|
||||
await this.StateManager.SaveStateAsync();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -171,5 +188,151 @@ namespace Microsoft.Actions.Actors.Runtime
|
|||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers a reminder with the actor.
|
||||
/// </summary>
|
||||
/// <param name="reminderName">The name of the reminder to register. The name must be unique per actor.</param>
|
||||
/// <param name="state">User state passed to the reminder invocation.</param>
|
||||
/// <param name="dueTime">The amount of time to delay before invoking the reminder for the first time. Specify negative one (-1) milliseconds to disable invocation. Specify zero (0) to invoke the reminder immediately after registration.
|
||||
/// </param>
|
||||
/// <param name="period">
|
||||
/// The time interval between reminder invocations after the first invocation. Specify negative one (-1) milliseconds to disable periodic invocation.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// A task that represents the asynchronous registration operation. The result of the task provides information about the registered reminder and is used to unregister the reminder using UnregisterReminderAsync />.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// The class deriving from <see cref="Microsoft.Actions.Actors.Runtime.Actor" /> must implement <see cref="Microsoft.Actions.Actors.Runtime.IRemindable" /> to consume reminder invocations. Multiple reminders can be registered at any time, uniquely identified by <paramref name="reminderName" />. Existing reminders can also be updated by calling this method again. Reminder invocations are synchronized both with other reminders and other actor method callbacks.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
protected async Task<IActorReminder> RegisterReminderAsync(
|
||||
string reminderName,
|
||||
byte[] state,
|
||||
TimeSpan dueTime,
|
||||
TimeSpan period)
|
||||
{
|
||||
var reminderInfo = new ReminderInfo(state, dueTime, period);
|
||||
var reminder = new ActorReminder(this.Id, reminderName, reminderInfo);
|
||||
await ActorRuntime.ActionsInteractor.RegisterReminderAsync(this.actorImplementaionTypeName, this.Id.ToString(), reminderName, reminderInfo.SerializeToJson());
|
||||
return reminder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unregisters a reminder previously registered using <see cref="Microsoft.Actions.Actors.Runtime.Actor.RegisterReminderAsync" />.
|
||||
/// </summary>
|
||||
/// <param name="reminder">The actor reminder to unregister.</param>
|
||||
/// <returns>
|
||||
/// Returns a task that represents the asynchronous unregistration operation.
|
||||
/// </returns>
|
||||
protected Task UnregisterReminderAsync(IActorReminder reminder)
|
||||
{
|
||||
return ActorRuntime.ActionsInteractor.UnregisterReminderAsync(this.actorImplementaionTypeName, this.Id.ToString(), reminder.Name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unregisters a reminder previously registered using <see cref="Microsoft.Actions.Actors.Runtime.Actor.RegisterReminderAsync" />.
|
||||
/// </summary>
|
||||
/// <param name="reminderName">The actor reminder anme to unregister.</param>
|
||||
/// <returns>
|
||||
/// Returns a task that represents the asynchronous unregistration operation.
|
||||
/// </returns>
|
||||
protected Task UnregisterReminderAsync(string reminderName)
|
||||
{
|
||||
return ActorRuntime.ActionsInteractor.UnregisterReminderAsync(this.actorImplementaionTypeName, this.Id.ToString(), reminderName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers a Timer for the actor. A timer name is autogenerated by the runtime to keep track of it.
|
||||
/// </summary>
|
||||
/// <param name="asyncCallback">
|
||||
/// A delegate that specifies a method to be called when the timer fires.
|
||||
/// It has one parameter: the state object passed to RegisterTimer.
|
||||
/// It returns a <see cref="System.Threading.Tasks.Task"/> representing the asynchronous operation.
|
||||
/// </param>
|
||||
/// <param name="state">An object containing information to be used by the callback method, or null.</param>
|
||||
/// <param name="dueTime">The amount of time to delay before the async callback is first invoked.
|
||||
/// Specify negative one (-1) milliseconds to prevent the timer from starting.
|
||||
/// Specify zero (0) to start the timer immediately.
|
||||
/// </param>
|
||||
/// <param name="period">
|
||||
/// The time interval between invocations of the async callback.
|
||||
/// Specify negative one (-1) milliseconds to disable periodic signaling.</param>
|
||||
/// <returns>Returns IActorTimer object.</returns>
|
||||
protected Task<IActorTimer> RegisterTimerAsync(
|
||||
Func<object, Task> asyncCallback,
|
||||
object state,
|
||||
TimeSpan dueTime,
|
||||
TimeSpan period)
|
||||
{
|
||||
return this.RegisterTimerAsync(null, asyncCallback, state, dueTime, period);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers a Timer for the actor. If a timer name is not provided, a timer is autogenerated.
|
||||
/// </summary>
|
||||
/// <param name="timerName">Timer Name. If a timer name is not provided, a timer is autogenerated.</param>
|
||||
/// <param name="asyncCallback">
|
||||
/// A delegate that specifies a method to be called when the timer fires.
|
||||
/// It has one parameter: the state object passed to RegisterTimer.
|
||||
/// It returns a <see cref="System.Threading.Tasks.Task"/> representing the asynchronous operation.
|
||||
/// </param>
|
||||
/// <param name="state">An object containing information to be used by the callback method, or null.</param>
|
||||
/// <param name="dueTime">The amount of time to delay before the async callback is first invoked.
|
||||
/// Specify negative one (-1) milliseconds to prevent the timer from starting.
|
||||
/// Specify zero (0) to start the timer immediately.
|
||||
/// </param>
|
||||
/// <param name="period">
|
||||
/// The time interval between invocations of the async callback.
|
||||
/// Specify negative one (-1) milliseconds to disable periodic signaling.</param>
|
||||
/// <returns>Returns IActorTimer object.</returns>
|
||||
protected async Task<IActorTimer> RegisterTimerAsync(
|
||||
string timerName,
|
||||
Func<object, Task> asyncCallback,
|
||||
object state,
|
||||
TimeSpan dueTime,
|
||||
TimeSpan period)
|
||||
{
|
||||
// create a timer name to register with Actions runtime.
|
||||
if (string.IsNullOrEmpty(timerName))
|
||||
{
|
||||
timerName = $"{this.Id.ToString()}_Timer_{this.timers.Count + 1}";
|
||||
}
|
||||
|
||||
var actorTimer = new ActorTimer(this, timerName, asyncCallback, state, dueTime, period);
|
||||
await ActorRuntime.ActionsInteractor.RegisterTimerAsync(this.actorImplementaionTypeName, this.Id.ToString(), timerName, actorTimer.SerializeToJson());
|
||||
|
||||
this.timers[timerName] = actorTimer;
|
||||
return actorTimer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unregisters a Timer previously set on this actor.
|
||||
/// </summary>
|
||||
/// <param name="timer">An IActorTimer representing timer that needs to be unregistered.</param>
|
||||
/// <returns>Task representing the Unregister timer operation.</returns>
|
||||
protected async Task UnregisterTimerAsync(IActorTimer timer)
|
||||
{
|
||||
await ActorRuntime.ActionsInteractor.UnregisterTimerAsync(this.actorImplementaionTypeName, this.Id.ToString(), timer.Name);
|
||||
if (this.timers.ContainsKey(timer.Name))
|
||||
{
|
||||
this.timers.Remove(timer.Name);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unregisters a Timer previously set on this actor.
|
||||
/// </summary>
|
||||
/// <param name="timerName">Name of timer to unregister.</param>
|
||||
/// <returns>Task representing the Unregister timer operation.</returns>
|
||||
protected async Task UnregisterTimerAsync(string timerName)
|
||||
{
|
||||
await ActorRuntime.ActionsInteractor.UnregisterTimerAsync(this.actorImplementaionTypeName, this.Id.ToString(), timerName);
|
||||
if (this.timers.ContainsKey(timerName))
|
||||
{
|
||||
this.timers.Remove(timerName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,8 +4,277 @@
|
|||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors.Runtime
|
||||
{
|
||||
internal sealed class ActorManager
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Actions.Actors;
|
||||
using Microsoft.Actions.Actors.Communication;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
/// <summary>
|
||||
/// Manages Actors of a specific actor type.
|
||||
/// </summary>
|
||||
internal sealed class ActorManager : IActorManager
|
||||
{
|
||||
private const string TraceType = "ActorManager";
|
||||
private const string ReceiveReminderMethodName = "ReceiveReminderAsync";
|
||||
private const string TimerMethodName = "FireTimerAsync";
|
||||
private readonly ActorService actorService;
|
||||
private readonly ConcurrentDictionary<ActorId, Actor> activeActors;
|
||||
private readonly ActorMethodContext reminderMethodContext;
|
||||
private readonly ActorMethodContext timerMethodContext;
|
||||
private readonly ActorMessageSerializersManager serializersManager;
|
||||
private readonly IActorMessageBodyFactory messageBodyFactory;
|
||||
|
||||
// method dispatchermap used by remoting calls.
|
||||
private readonly ActorMethodDispatcherMap methodDispatcherMap;
|
||||
|
||||
// method info map used by non-remoting calls.
|
||||
private readonly ActorMethodInfoMap actorMethodInfoMap;
|
||||
|
||||
internal ActorManager(ActorService actorService)
|
||||
{
|
||||
this.actorService = actorService;
|
||||
|
||||
// map for remoting calls.
|
||||
this.methodDispatcherMap = new ActorMethodDispatcherMap(this.actorService.ActorTypeInfo.InterfaceTypes);
|
||||
|
||||
// map for non-remoting calls.
|
||||
this.actorMethodInfoMap = new ActorMethodInfoMap(this.actorService.ActorTypeInfo.InterfaceTypes);
|
||||
this.activeActors = new ConcurrentDictionary<ActorId, Actor>();
|
||||
this.reminderMethodContext = ActorMethodContext.CreateForReminder(ReceiveReminderMethodName);
|
||||
this.timerMethodContext = ActorMethodContext.CreateForReminder(TimerMethodName);
|
||||
this.serializersManager = IntializeSerializationManager(null);
|
||||
this.messageBodyFactory = new WrappedRequestMessageFactory();
|
||||
}
|
||||
|
||||
internal ActorTypeInformation ActorTypeInfo => this.actorService.ActorTypeInfo;
|
||||
|
||||
internal Task<Tuple<string, byte[]>> DispatchWithRemotingAsync(ActorId actorId, string actorMethodName, string actionsActorheader, Stream data, CancellationToken cancellationToken)
|
||||
{
|
||||
var actorMethodContext = ActorMethodContext.CreateForActor(actorMethodName);
|
||||
|
||||
// Get the serialized header
|
||||
var actorMessageHeader = this.serializersManager.GetHeaderSerializer()
|
||||
.DeserializeRequestHeaders(new MemoryStream(Encoding.ASCII.GetBytes(actionsActorheader)));
|
||||
|
||||
var interfaceId = actorMessageHeader.InterfaceId;
|
||||
|
||||
// Get the deserialized Body.
|
||||
var msgBodySerializer = this.serializersManager.GetRequestMessageBodySerializer(actorMessageHeader.InterfaceId);
|
||||
var actorMessageBody = msgBodySerializer.Deserialize(data);
|
||||
|
||||
// Call the method on the method dispatcher using the Func below.
|
||||
var methodDispatcher = this.methodDispatcherMap.GetDispatcher(actorMessageHeader.InterfaceId, actorMessageHeader.MethodId);
|
||||
|
||||
// Create a Func to be invoked by common method.
|
||||
async Task<Tuple<string, byte[]>> RequestFunc(Actor actor, CancellationToken ct)
|
||||
{
|
||||
IActorResponseMessageBody responseMsgBody = null;
|
||||
|
||||
try
|
||||
{
|
||||
responseMsgBody = (IActorResponseMessageBody)await methodDispatcher.DispatchAsync(
|
||||
actor,
|
||||
actorMessageHeader.MethodId,
|
||||
actorMessageBody,
|
||||
this.messageBodyFactory,
|
||||
ct);
|
||||
|
||||
return this.CreateResponseMessage(responseMsgBody, interfaceId);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
// return exception response message
|
||||
return this.CreateExceptionResponseMessage(exception);
|
||||
}
|
||||
}
|
||||
|
||||
return this.DispatchInternalAsync(actorId, actorMethodContext, RequestFunc, cancellationToken);
|
||||
}
|
||||
|
||||
internal async Task DispatchWithoutRemotingAsync(ActorId actorId, string actorMethodName, Stream requestBodyStream, Stream responseBodyStream, CancellationToken cancellationToken)
|
||||
{
|
||||
var actorMethodContext = ActorMethodContext.CreateForActor(actorMethodName);
|
||||
var serializer = new JsonSerializer();
|
||||
|
||||
// Create a Func to be invoked by common method.
|
||||
var methodInfo = this.actorMethodInfoMap.LookupActorMethodInfo(actorMethodName);
|
||||
|
||||
async Task<object> RequestFunc(Actor actor, CancellationToken ct)
|
||||
{
|
||||
var parameters = methodInfo.GetParameters();
|
||||
dynamic awaitable;
|
||||
|
||||
if (parameters.Length == 0)
|
||||
{
|
||||
awaitable = methodInfo.Invoke(actor, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
// deserialize using stream.
|
||||
var type = parameters[0].ParameterType;
|
||||
var deserializedType = default(object);
|
||||
using (var streamReader = new StreamReader(requestBodyStream))
|
||||
{
|
||||
using (var reader = new JsonTextReader(streamReader))
|
||||
{
|
||||
deserializedType = serializer.Deserialize(reader, type);
|
||||
}
|
||||
}
|
||||
|
||||
awaitable = methodInfo.Invoke(actor, new object[] { deserializedType });
|
||||
}
|
||||
|
||||
await awaitable;
|
||||
|
||||
// Write Response back if method's return type is other than Task.
|
||||
// Serialize result if it has result (return type was not just Task.)
|
||||
if (methodInfo.ReturnType.Name != typeof(Task).Name)
|
||||
{
|
||||
// already await, Getting result will be non blocking.
|
||||
var x = awaitable.GetAwaiter().GetResult();
|
||||
return x;
|
||||
}
|
||||
else
|
||||
{
|
||||
return default(object);
|
||||
}
|
||||
}
|
||||
|
||||
var result = await this.DispatchInternalAsync(actorId, actorMethodContext, RequestFunc, cancellationToken);
|
||||
|
||||
using (var streamWriter = new StreamWriter(responseBodyStream))
|
||||
{
|
||||
using (var writer = new JsonTextWriter(streamWriter))
|
||||
{
|
||||
serializer.Serialize(writer, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal Task FireReminderAsync(ActorId actorId, string reminderName, Stream requestBodyStream, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
// Only FireReminder if its IRemindable, else ignore it.
|
||||
if (this.ActorTypeInfo.IsRemindable)
|
||||
{
|
||||
var reminderdata = ReminderInfo.Deserialize(requestBodyStream);
|
||||
|
||||
// Create a Func to be invoked by common method.
|
||||
async Task<byte[]> RequestFunc(Actor actor, CancellationToken ct)
|
||||
{
|
||||
await
|
||||
(actor as IRemindable).ReceiveReminderAsync(
|
||||
reminderName,
|
||||
reminderdata.Data,
|
||||
reminderdata.DueTime,
|
||||
reminderdata.Period);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.DispatchInternalAsync(actorId, this.reminderMethodContext, RequestFunc, cancellationToken);
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
internal Task FireTimerAsync(ActorId actorId, string timerName, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
// Create a Func to be invoked by common method.
|
||||
async Task<byte[]> RequestFunc(Actor actor, CancellationToken ct)
|
||||
{
|
||||
await
|
||||
actor.FireTimerAsync(timerName);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.DispatchInternalAsync(actorId, this.timerMethodContext, RequestFunc, cancellationToken);
|
||||
}
|
||||
|
||||
internal async Task ActivateActor(ActorId actorId)
|
||||
{
|
||||
// An actor is activated by "actions" runtime when a call is to be made for an actor.
|
||||
var actor = this.actorService.CreateActor(actorId);
|
||||
await actor.OnActivateInternalAsync();
|
||||
|
||||
// Add actor to activeActors only after OnActivate succeeds (user code can throw error from its override of Activate method.)
|
||||
// Always add the new instance.
|
||||
this.activeActors.AddOrUpdate(actorId, actor, (key, oldValue) => actor);
|
||||
}
|
||||
|
||||
internal async Task DeactivateActor(ActorId actorId)
|
||||
{
|
||||
if (this.activeActors.TryRemove(actorId, out var deactivatedActor))
|
||||
{
|
||||
await deactivatedActor.OnDeactivateInternalAsync();
|
||||
}
|
||||
}
|
||||
|
||||
private static ActorMessageSerializersManager IntializeSerializationManager(
|
||||
IActorMessageBodySerializationProvider serializationProvider)
|
||||
{
|
||||
// TODO serializer settings
|
||||
return new ActorMessageSerializersManager(
|
||||
serializationProvider,
|
||||
new ActorMessageHeaderSerializer());
|
||||
}
|
||||
|
||||
private async Task<T> DispatchInternalAsync<T>(ActorId actorId, ActorMethodContext actorMethodContext, Func<Actor, CancellationToken, Task<T>> actorFunc, CancellationToken cancellationToken)
|
||||
{
|
||||
if (!this.activeActors.TryGetValue(actorId, out var actor))
|
||||
{
|
||||
// This should never happen, as "Actions" runtime activates the actor first. if it ever it would mean a bug in "Actions" runtime.
|
||||
var errorMsg = $"Actor {actorId} is not yet activated.";
|
||||
ActorTrace.Instance.WriteError(TraceType, errorMsg);
|
||||
throw new InvalidOperationException(errorMsg);
|
||||
}
|
||||
|
||||
var retval = default(T);
|
||||
|
||||
try
|
||||
{
|
||||
// invoke the function of the actor
|
||||
await actor.OnPreActorMethodAsyncInternal(actorMethodContext);
|
||||
retval = await actorFunc.Invoke(actor, cancellationToken);
|
||||
await actor.OnPostActorMethodAsyncInternal(actorMethodContext);
|
||||
}
|
||||
catch
|
||||
{
|
||||
actor.OnInvokeFailed();
|
||||
throw;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
private Tuple<string, byte[]> CreateResponseMessage(IActorResponseMessageBody msgBody, int interfaceId)
|
||||
{
|
||||
var responseMsgBodyBytes = new byte[0];
|
||||
if (msgBody != null)
|
||||
{
|
||||
var responseSerializer = this.serializersManager.GetResponseMessageBodySerializer(interfaceId);
|
||||
responseMsgBodyBytes = responseSerializer.Serialize(msgBody);
|
||||
}
|
||||
|
||||
return new Tuple<string, byte[]>(string.Empty, responseMsgBodyBytes);
|
||||
}
|
||||
|
||||
private Tuple<string, byte[]> CreateExceptionResponseMessage(Exception ex)
|
||||
{
|
||||
var responseHeader = new ActorResponseMessageHeader();
|
||||
responseHeader.AddHeader("HasRemoteException", new byte[0]);
|
||||
var responseHeaderBytes = this.serializersManager.GetHeaderSerializer().SerializeResponseHeader(responseHeader);
|
||||
var serializedHeader = Encoding.UTF8.GetString(responseHeaderBytes, 0, responseHeaderBytes.Length);
|
||||
|
||||
var responseMsgBody = RemoteException.FromException(ex);
|
||||
|
||||
return new Tuple<string, byte[]>(serializedHeader, responseMsgBody);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,43 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Actions.Actors.Runtime
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
|
||||
/// <summary>
|
||||
/// Actor method dispatcher map for non remoting calls. method_name -> MethodInfo for methods defined in IACtor interfaces.
|
||||
/// </summary>
|
||||
internal class ActorMethodInfoMap
|
||||
{
|
||||
private readonly Dictionary<string, MethodInfo> methods;
|
||||
|
||||
public ActorMethodInfoMap(IEnumerable<Type> interfaceTypes)
|
||||
{
|
||||
this.methods = new Dictionary<string, MethodInfo>();
|
||||
|
||||
// Find methods which are defined in IActor interface.
|
||||
foreach (var actorInterface in interfaceTypes)
|
||||
{
|
||||
foreach (var methodInfo in actorInterface.GetMethods())
|
||||
{
|
||||
this.methods.Add(methodInfo.Name, methodInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public MethodInfo LookupActorMethodInfo(string methodName)
|
||||
{
|
||||
if (!this.methods.TryGetValue(methodName, out var methodInfo))
|
||||
{
|
||||
throw new MissingMethodException($"Actor type doesn't contain method {methodName}");
|
||||
}
|
||||
|
||||
return methodInfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue