diff --git a/src/Microsoft.Actions.Actors/ActionsHttpInteractor.cs b/src/Microsoft.Actions.Actors/ActionsHttpInteractor.cs
index d72cac61..79288739 100644
--- a/src/Microsoft.Actions.Actors/ActionsHttpInteractor.cs
+++ b/src/Microsoft.Actions.Actors/ActionsHttpInteractor.cs
@@ -17,13 +17,14 @@ namespace Microsoft.Actions.Actors
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Actions.Actors.Communication;
+ using Microsoft.Actions.Actors.Runtime;
using Newtonsoft.Json;
///
/// Class to interact with actions runtime over http.
///
internal class ActionsHttpInteractor : IActionsInteractor
- {
+ {
private const string ActionsEndpoint = Constants.ActionsDefaultEndpoint;
private readonly string actionsPort = Constants.ActionsDefaultPort;
private readonly HttpClientHandler innerHandler;
diff --git a/src/Microsoft.Actions.Actors/Client/ActorProxyFactory.cs b/src/Microsoft.Actions.Actors/Client/ActorProxyFactory.cs
index a28a4c41..069600e3 100644
--- a/src/Microsoft.Actions.Actors/Client/ActorProxyFactory.cs
+++ b/src/Microsoft.Actions.Actors/Client/ActorProxyFactory.cs
@@ -16,15 +16,19 @@ namespace Microsoft.Actions.Actors.Client
///
internal class ActorProxyFactory : IActorProxyFactory
{
- // Used only for Remoting based communication
- private static readonly ActorCommunicationClientFactory DefaultActorCommunicationClientFactory = new ActorCommunicationClientFactory();
+ private readonly object thisLock;
+ private volatile IActorCommunicationClientFactory actorCommunicationClientFactory;
+
///
/// Initializes a new instance of the class.
/// TODO: Accept Retry settings.
///
public ActorProxyFactory()
{
+ this.thisLock = new object();
+
+ this.actorCommunicationClientFactory = null;
}
///
@@ -35,7 +39,7 @@ namespace Microsoft.Actions.Actors.Client
}
///
- /// Create a proxy, this method is also used by ActorReference also to create proxy.
+ /// Create a proxy, this method is also sued by ACtorReference also to create proxy.
///
/// Actor Id.
/// Actor Interface Type.
@@ -43,14 +47,49 @@ namespace Microsoft.Actions.Actors.Client
/// Returns Actor Proxy.
internal object CreateActorProxy(ActorId actorId, Type actorInterfaceType, string actorType)
{
- // TODO factory/client level settings
- var actorCommunicationClient = DefaultActorCommunicationClientFactory.GetClient(actorId, actorType);
+ var factory = this.GetOrCreateActorCommunicationClientFactory();
+
+ // TODO factory level settings or method level parameter, default http
+ var actorCommunicationClient = new ActorCommunicationClient(
+ factory,
+ actorId,
+ actorType);
var proxyGenerator = ActorCodeBuilder.GetOrCreateProxyGenerator(actorInterfaceType);
return proxyGenerator.CreateActorProxy(
actorCommunicationClient,
- DefaultActorCommunicationClientFactory.GetRemotingMessageBodyFactory());
+ factory.GetRemotingMessageBodyFactory());
}
+
+ private IActorCommunicationClientFactory GetOrCreateActorCommunicationClientFactory()
+ {
+ if (this.actorCommunicationClientFactory != null)
+ {
+ return this.actorCommunicationClientFactory;
+ }
+
+ lock (this.thisLock)
+ {
+ if (this.actorCommunicationClientFactory == null)
+ {
+ this.actorCommunicationClientFactory = this.CreateActorCommunicationClientFactory();
+ }
+ }
+
+ return this.actorCommunicationClientFactory;
+ }
+
+ private IActorCommunicationClientFactory CreateActorCommunicationClientFactory()
+ {
+ // TODO factory settings
+ var factory = new ActorCommunicationClientFactory();
+ if (factory == null)
+ {
+ throw new NotSupportedException("ClientFactory can't be null");
+ }
+
+ return factory;
+ }
}
}
\ No newline at end of file
diff --git a/src/Microsoft.Actions.Actors/Communication/Client/ActorCommunicationClient.cs b/src/Microsoft.Actions.Actors/Communication/Client/ActorCommunicationClient.cs
index 2afc20bf..f1ec0dde 100644
--- a/src/Microsoft.Actions.Actors/Communication/Client/ActorCommunicationClient.cs
+++ b/src/Microsoft.Actions.Actors/Communication/Client/ActorCommunicationClient.cs
@@ -11,16 +11,21 @@ namespace Microsoft.Actions.Actors.Communication.Client
internal class ActorCommunicationClient : IActorCommunicationClient
{
- private readonly IActionsInteractor actionsInteractor;
+ private readonly SemaphoreSlim communicationClientLock;
+ private readonly IActorCommunicationClientFactory communicationClientFactory;
+ private readonly IActorMessageBodyFactory messageBodyFactory;
+ private IActionsInteractor actionsInteractor;
public ActorCommunicationClient(
- IActionsInteractor actionsInteractor,
+ IActorCommunicationClientFactory remotingClientFactory,
ActorId actorId,
string actorType)
{
this.ActorId = actorId;
this.ActorType = actorType;
- this.actionsInteractor = actionsInteractor;
+ this.communicationClientFactory = remotingClientFactory;
+ this.communicationClientLock = new SemaphoreSlim(1);
+ this.messageBodyFactory = remotingClientFactory.GetRemotingMessageBodyFactory();
}
///
@@ -41,7 +46,33 @@ namespace Microsoft.Actions.Actors.Communication.Client
string methodName,
CancellationToken cancellationToken)
{
- return await this.actionsInteractor.InvokeActorMethodWithRemotingAsync(remotingRequestMessage);
+ var client = await this.GetCommunicationClientAsync(cancellationToken);
+ return await client.InvokeActorMethodWithRemotingAsync(remotingRequestMessage);
+ }
+
+ private async Task GetCommunicationClientAsync(CancellationToken cancellationToken)
+ {
+ IActionsInteractor client;
+ await this.communicationClientLock.WaitAsync(cancellationToken);
+ try
+ {
+ if (this.actionsInteractor == null)
+ {
+ this.actionsInteractor = await this.communicationClientFactory.GetClientAsync();
+ }
+
+ client = this.actionsInteractor;
+ }
+ finally
+ {
+ // Release the lock incase of exceptions from the GetClientAsync method, which can
+ // happen if there are non retriable exceptions in that method. Eg: There can be
+ // ServiceNotFoundException if the GetClientAsync client is called before the
+ // service creation completes.
+ this.communicationClientLock.Release();
+ }
+
+ return client;
}
}
}
diff --git a/src/Microsoft.Actions.Actors/Communication/Client/ActorCommunicationClientFactory.cs b/src/Microsoft.Actions.Actors/Communication/Client/ActorCommunicationClientFactory.cs
index 0afccfa3..4c44516a 100644
--- a/src/Microsoft.Actions.Actors/Communication/Client/ActorCommunicationClientFactory.cs
+++ b/src/Microsoft.Actions.Actors/Communication/Client/ActorCommunicationClientFactory.cs
@@ -5,19 +5,24 @@
namespace Microsoft.Actions.Actors.Communication.Client
{
+ using System;
+ using System.Globalization;
+ using System.Threading;
+ using System.Threading.Tasks;
+ using Microsoft.Actions.Actors.Runtime;
+
///
/// An that uses
/// http protocol to create that communicate with actors.
///
internal class ActorCommunicationClientFactory : IActorCommunicationClientFactory
{
- private static readonly IActionsInteractor ActionsInteractor = new ActionsHttpInteractor();
private readonly ActorMessageSerializersManager serializersManager;
- private readonly IActorMessageBodyFactory actorMessageBodyFactory = null;
+ private readonly IActorMessageBodyFactory remotingMessageBodyFactory = null;
///
/// Initializes a new instance of the class.
- /// Constructs actor remoting communication client factory.
+ /// Constructs a fabric transport based service remoting client factory.
///
/// IActorCommunicationMessageSerializationProvider provider.
public ActorCommunicationClientFactory(
@@ -25,7 +30,19 @@ namespace Microsoft.Actions.Actors.Communication.Client
{
// TODO Add settings, exception handlers, serialization provider
this.serializersManager = IntializeSerializationManager(serializationProvider);
- this.actorMessageBodyFactory = this.serializersManager.GetSerializationProvider().CreateMessageBodyFactory();
+ this.remotingMessageBodyFactory = this.serializersManager.GetSerializationProvider().CreateMessageBodyFactory();
+ }
+
+ ///
+ /// Returns a client to communicate.
+ ///
+ ///
+ /// A Task that represents outstanding operation. The result of the Task is
+ /// the CommunicationClient() object.
+ ///
+ public async Task GetClientAsync()
+ {
+ return await this.CreateClientAsync();
}
///
@@ -34,12 +51,7 @@ namespace Microsoft.Actions.Actors.Communication.Client
/// A factory for creating the remoting message bodies.
public IActorMessageBodyFactory GetRemotingMessageBodyFactory()
{
- return this.actorMessageBodyFactory;
- }
-
- public ActorCommunicationClient GetClient(ActorId actorId, string actorType)
- {
- return new ActorCommunicationClient(ActionsInteractor, actorId, actorType);
+ return this.remotingMessageBodyFactory;
}
private static ActorMessageSerializersManager IntializeSerializationManager(
@@ -50,5 +62,28 @@ namespace Microsoft.Actions.Actors.Communication.Client
serializationProvider,
new ActorMessageHeaderSerializer());
}
+
+ ///
+ /// Creates a communication client for the given endpoint address.
+ ///
+ /// The communication client that was created.
+ private Task CreateClientAsync()
+ {
+ try
+ {
+ // TODO add retries and error handling - add CreateClientWithRetriesAsync version
+ var client = new ActionsHttpInteractor(
+ this.serializersManager);
+ return Task.FromResult((IActionsInteractor)client);
+ }
+ catch (Exception ex)
+ {
+ // TODO specific error handling
+ throw new Exception(
+ string.Format(
+ CultureInfo.CurrentCulture,
+ ex.ToString()));
+ }
+ }
}
}
diff --git a/src/Microsoft.Actions.Actors/Communication/Client/IActorCommunicationClientFactory.cs b/src/Microsoft.Actions.Actors/Communication/Client/IActorCommunicationClientFactory.cs
index 2923e03f..5f130d51 100644
--- a/src/Microsoft.Actions.Actors/Communication/Client/IActorCommunicationClientFactory.cs
+++ b/src/Microsoft.Actions.Actors/Communication/Client/IActorCommunicationClientFactory.cs
@@ -5,6 +5,10 @@
namespace Microsoft.Actions.Actors.Communication.Client
{
+ using System.Threading;
+ using System.Threading.Tasks;
+ using Microsoft.Actions.Actors.Runtime;
+
///
/// A factory for creating actions communication clients..
///
@@ -17,11 +21,12 @@ namespace Microsoft.Actions.Actors.Communication.Client
IActorMessageBodyFactory GetRemotingMessageBodyFactory();
///
- /// Gets actor communication client.
+ /// Get a communication client.
///
- /// Actor Id.
- /// Actor Type.
- /// A factory for creating the remoting message bodies.
- ActorCommunicationClient GetClient(ActorId actorId, string actorType);
+ ///
+ /// A Task that represents outstanding operation. The result of the Task is
+ /// the CommunicationClient() object.
+ ///
+ Task GetClientAsync();
}
}