Skip to content

Commit

Permalink
Non-static GrainClient #2822
Browse files Browse the repository at this point in the history
  • Loading branch information
jdom authored Mar 10, 2017
2 parents 5909095 + 61018c2 commit 430a1de
Show file tree
Hide file tree
Showing 117 changed files with 1,952 additions and 1,001 deletions.
2 changes: 2 additions & 0 deletions src/NuGet/Microsoft.Orleans.Core.nuspec
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
<dependency id="Newtonsoft.Json" version="9.0.1" />
<dependency id="System.Collections.Immutable" version="1.1.37" />
<dependency id="System.Reflection.Metadata" version="1.2.0" />
<dependency id="Microsoft.Extensions.DependencyInjection" version="1.0.0" />
<dependency id="Microsoft.Extensions.DependencyInjection.Abstractions" version="1.0.0" />
</dependencies>
</metadata>
<files>
Expand Down
10 changes: 7 additions & 3 deletions src/Orleans/Async/UnobservedExceptionsHandlerClass.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,20 @@ static UnobservedExceptionsHandlerClass()
}
}

internal static void SetUnobservedExceptionHandler(UnobservedExceptionDelegate handler)
internal static bool TrySetUnobservedExceptionHandler(UnobservedExceptionDelegate handler)
{
if (handler == null) throw new ArgumentNullException(nameof(handler));
lock (lockObject)
{
if (unobservedExceptionHandler != null && handler != null)
if (unobservedExceptionHandler != null)
{
throw new InvalidOperationException("Calling SetUnobservedExceptionHandler the second time.");
return false;
}

unobservedExceptionHandler = handler;
}

return true;
}

internal static void ResetUnobservedExceptionHandler()
Expand Down
94 changes: 94 additions & 0 deletions src/Orleans/Core/ClientBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
using System;
using System.Collections.Generic;
using Microsoft.Extensions.DependencyInjection;
using Orleans.CodeGeneration;
using Orleans.Messaging;
using Orleans.Providers;
using Orleans.Runtime;
using Orleans.Runtime.Configuration;
using Orleans.Serialization;
using Orleans.Streams;

namespace Orleans
{
/// <summary>
/// Builder used for creating <see cref="IClusterClient"/> instances.
/// </summary>
public class ClientBuilder : IClientBuilder
{
private readonly List<Action<IServiceCollection>> configureServicesDelegates = new List<Action<IServiceCollection>>();
private bool built;
private ClientConfiguration clientConfiguration;

/// <inheritdoc />
public IClusterClient Build()
{
if (this.built) throw new InvalidOperationException($"{nameof(this.Build)} may only be called once per {nameof(ClientBuilder)} instance.");
this.built = true;

// If no configuration has been specified, use a default instance.
this.clientConfiguration = this.clientConfiguration ?? ClientConfiguration.StandardLoad();

// Configure the container.
var services = new ServiceCollection();
AddBasicServices(services, this.clientConfiguration);
foreach (var configureServices in this.configureServicesDelegates)
{
configureServices(services);
}

// Build the container and configure the runtime client.
var serviceProvider = services.BuildServiceProvider();
serviceProvider.GetRequiredService<OutsideRuntimeClient>().ConsumeServices(serviceProvider);

// Construct and return the cluster client.
var result = serviceProvider.GetRequiredService<IClusterClient>();
return result;
}

/// <inheritdoc />
public IClientBuilder UseConfiguration(ClientConfiguration configuration)
{
if (configuration == null) throw new ArgumentNullException(nameof(configuration));
if (this.clientConfiguration != null) throw new InvalidOperationException("Base configuration has already been specified and cannot be overridden.");
this.clientConfiguration = configuration;
return this;
}

/// <inheritdoc />
public IClientBuilder ConfigureServices(Action<IServiceCollection> configureServices)
{
if (configureServices == null) throw new ArgumentNullException(nameof(configureServices));
this.configureServicesDelegates.Add(configureServices);
return this;
}

private static void AddBasicServices(IServiceCollection services, ClientConfiguration clientConfiguration)
{
services.AddSingleton(clientConfiguration);
services.AddSingleton<TypeMetadataCache>();
services.AddSingleton<AssemblyProcessor>();
services.AddSingleton<OutsideRuntimeClient>();
services.AddFromExisting<IRuntimeClient, OutsideRuntimeClient>();
services.AddFromExisting<IClusterConnectionStatusListener, OutsideRuntimeClient>();
services.AddSingleton<GrainFactory>();
services.AddFromExisting<IGrainFactory, GrainFactory>();
services.AddFromExisting<IInternalGrainFactory, GrainFactory>();
services.AddFromExisting<IGrainReferenceConverter, GrainFactory>();
services.AddSingleton<ClientProviderRuntime>();
services.AddFromExisting<IMessagingConfiguration, ClientConfiguration>();
services.AddFromExisting<ITraceConfiguration, ClientConfiguration>();
services.AddSingleton<IGatewayListProvider>(
sp => ActivatorUtilities.CreateInstance<GatewayProviderFactory>(sp).CreateGatewayListProvider());
services.AddSingleton<SerializationManager>();
services.AddSingleton<MessageFactory>();
services.AddSingleton<Func<string, Logger>>(LogManager.GetLogger);
services.AddSingleton<StreamProviderManager>();
services.AddSingleton<ClientStatisticsManager>();
services.AddFromExisting<IStreamProviderManager, StreamProviderManager>();
services.AddSingleton<CodeGeneratorManager>();
services.AddSingleton<IInternalClusterClient, ClusterClient>();
services.AddFromExisting<IClusterClient, IInternalClusterClient>();
}
}
}
108 changes: 108 additions & 0 deletions src/Orleans/Core/ClientBuilderExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
using System;
using System.IO;
using Microsoft.Extensions.DependencyInjection;
using Orleans.CodeGeneration;
using Orleans.Runtime.Configuration;

namespace Orleans
{
/// <summary>
/// Extension methods for <see cref="IClientBuilder"/>.
/// </summary>
public static class ClientBuilderExtensions
{
/// <summary>
/// Loads configuration from the standard client configuration locations.
/// </summary>
/// <param name="builder">The builder.</param>
/// <remarks>
/// This method loads the first client configuration file it finds, searching predefined directories for predefined file names.
/// The following file names are tried in order:
/// <list type="number">
/// <item>ClientConfiguration.xml</item>
/// <item>OrleansClientConfiguration.xml</item>
/// <item>Client.config</item>
/// <item>Client.xml</item>
/// </list>
/// The following directories are searched in order:
/// <list type="number">
/// <item>The directory of the executing assembly.</item>
/// <item>The approot directory.</item>
/// <item>The current working directory.</item>
/// <item>The parent of the current working directory.</item>
/// </list>
/// Each directory is searched for all configuration file names before proceeding to the next directory.
/// </remarks>
/// <returns>The builder.</returns>
public static IClientBuilder LoadConfiguration(this IClientBuilder builder)
{
builder.UseConfiguration(ClientConfiguration.StandardLoad());
return builder;
}

/// <summary>
/// Loads configuration from the provided location.
/// </summary>
/// <param name="builder">The builder.</param>
/// <param name="configurationFilePath"></param>
/// <returns>The builder.</returns>
public static IClientBuilder LoadConfiguration(this IClientBuilder builder, string configurationFilePath)
{
builder.LoadConfiguration(new FileInfo(configurationFilePath));
return builder;
}

/// <summary>
/// Loads configuration from the provided location.
/// </summary>
/// <param name="builder">The builder.</param>
/// <param name="configurationFile"></param>
/// <returns>The builder.</returns>
public static IClientBuilder LoadConfiguration(this IClientBuilder builder, FileInfo configurationFile)
{
var config = ClientConfiguration.LoadFromFile(configurationFile.FullName);
if (config == null)
{
throw new ArgumentException(
$"Error loading client configuration file {configurationFile.FullName}",
nameof(configurationFile));
}

builder.UseConfiguration(config);
return builder;
}

/// <summary>
/// Adds a client invocation callback.
/// </summary>
/// <param name="builder">The builder.</param>
/// <param name="callback">The callback.</param>
/// <remarks>
/// A <see cref="ClientInvokeCallback"/> ia a global pre-call interceptor.
/// Synchronous callback made just before a message is about to be constructed and sent by a client to a grain.
/// This call will be made from the same thread that constructs the message to be sent, so any thread-local settings
/// such as <c>Orleans.RequestContext</c> will be picked up.
/// The action receives an <see cref="InvokeMethodRequest"/> with details of the method to be invoked, including InterfaceId and MethodId,
/// and a <see cref="IGrain"/> which is the GrainReference this request is being sent through
/// This callback method should return promptly and do a minimum of work, to avoid blocking calling thread or impacting throughput.
/// </remarks>
/// <returns>The builder.</returns>
public static IClientBuilder AddClientInvokeCallback(this IClientBuilder builder, ClientInvokeCallback callback)
{
builder.ConfigureServices(services => services.AddSingleton(callback));
return builder;
}

/// <summary>
/// Registers a <see cref="ConnectionToClusterLostHandler"/> event handler.
/// </summary>
/// <param name="builder">The builder.</param>
/// <param name="handler">The handler.</param>
/// <returns>The builder.</returns>
public static IClientBuilder AddClusterConnectionLostHandler(this IClientBuilder builder, ConnectionToClusterLostHandler handler)
{
builder.ConfigureServices(services => services.AddSingleton(handler));
return builder;
}
}
}
Loading

0 comments on commit 430a1de

Please sign in to comment.