Skip to content

Commit

Permalink
Expose DefaultGrainActivator and GrainConstructorArgumentFactory for …
Browse files Browse the repository at this point in the history
…customization
  • Loading branch information
ReubenBond committed Jul 19, 2023
1 parent edf4183 commit f0229bc
Show file tree
Hide file tree
Showing 5 changed files with 170 additions and 141 deletions.
30 changes: 30 additions & 0 deletions src/Orleans.Runtime/Activation/ConfigureDefaultGrainActivator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using System;
using Orleans.Metadata;

namespace Orleans.Runtime
{
internal class ConfigureDefaultGrainActivator : IConfigureGrainTypeComponents
{
private readonly IServiceProvider _serviceProvider;
private readonly GrainClassMap _grainClassMap;

public ConfigureDefaultGrainActivator(GrainClassMap grainClassMap, IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
_grainClassMap = grainClassMap;
}

public void Configure(GrainType grainType, GrainProperties properties, GrainTypeSharedContext shared)
{
if (shared.GetComponent<IGrainActivator>() is object) return;

if (!_grainClassMap.TryGetGrainClass(grainType, out var grainClass))
{
return;
}

var instanceActivator = new DefaultGrainActivator(_serviceProvider, grainClass);
shared.SetComponent<IGrainActivator>(instanceActivator);
}
}
}
47 changes: 47 additions & 0 deletions src/Orleans.Runtime/Activation/DefaultGrainActivator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using System;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;

namespace Orleans.Runtime
{
/// <summary>
/// The default <see cref="IGrainActivator"/> implementation.
/// </summary>
public class DefaultGrainActivator : IGrainActivator
{
private readonly ObjectFactory _grainInstanceFactory;
private readonly GrainConstructorArgumentFactory _argumentFactory;

/// <summary>
/// Initializes a new <see cref="DefaultGrainActivator"/> instance.
/// </summary>
/// <param name="serviceProvider">The service provider.</param>
/// <param name="grainClass">The grain class.</param>
public DefaultGrainActivator(IServiceProvider serviceProvider, Type grainClass)
{
_argumentFactory = new GrainConstructorArgumentFactory(serviceProvider, grainClass);
_grainInstanceFactory = ActivatorUtilities.CreateFactory(grainClass, _argumentFactory.ArgumentTypes);
}

/// <inheritdoc/>
public object CreateInstance(IGrainContext context)
{
var args = _argumentFactory.CreateArguments(context);
return _grainInstanceFactory(context.ActivationServices, args);
}

/// <inheritdoc/>
public async ValueTask DisposeInstance(IGrainContext context, object instance)
{
switch (instance)
{
case IAsyncDisposable asyncDisposable:
await asyncDisposable.DisposeAsync();
break;
case IDisposable disposable:
disposable.Dispose();
break;
}
}
}
}
60 changes: 0 additions & 60 deletions src/Orleans.Runtime/Activation/IGrainContextActivator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Orleans.Concurrency;
Expand Down Expand Up @@ -387,62 +385,4 @@ public bool MayInterleave(Message message)
return false;
}
}

internal class ConfigureDefaultGrainActivator : IConfigureGrainTypeComponents
{
private readonly GrainClassMap _grainClassMap;
private readonly ConstructorArgumentFactory _constructorArgumentFactory;

public ConfigureDefaultGrainActivator(GrainClassMap grainClassMap, IServiceProvider serviceProvider)
{
_constructorArgumentFactory = new ConstructorArgumentFactory(serviceProvider);
_grainClassMap = grainClassMap;
}

public void Configure(GrainType grainType, GrainProperties properties, GrainTypeSharedContext shared)
{
if (shared.GetComponent<IGrainActivator>() is object) return;

if (!_grainClassMap.TryGetGrainClass(grainType, out var grainClass))
{
return;
}

var argumentFactory = _constructorArgumentFactory.CreateFactory(grainClass);
var createGrainInstance = ActivatorUtilities.CreateFactory(grainClass, argumentFactory.ArgumentTypes);
var instanceActivator = new DefaultGrainActivator(createGrainInstance, argumentFactory);
shared.SetComponent<IGrainActivator>(instanceActivator);
}

internal class DefaultGrainActivator : IGrainActivator
{
private readonly ObjectFactory _factory;
private readonly ConstructorArgumentFactory.ArgumentFactory _argumentFactory;

public DefaultGrainActivator(ObjectFactory factory, ConstructorArgumentFactory.ArgumentFactory argumentFactory)
{
_factory = factory;
_argumentFactory = argumentFactory;
}

public object CreateInstance(IGrainContext context)
{
var args = _argumentFactory.CreateArguments(context);
return _factory(context.ActivationServices, args);
}

public async ValueTask DisposeInstance(IGrainContext context, object instance)
{
switch (instance)
{
case IAsyncDisposable asyncDisposable:
await asyncDisposable.DisposeAsync();
break;
case IDisposable disposable:
disposable.Dispose();
break;
}
}
}
}
}
81 changes: 0 additions & 81 deletions src/Orleans.Runtime/Facet/ConstructorArgumentFactory.cs

This file was deleted.

93 changes: 93 additions & 0 deletions src/Orleans.Runtime/Facet/GrainConstructorArgumentFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Reflection;
using Microsoft.Extensions.DependencyInjection;

namespace Orleans.Runtime
{
/// <summary>
/// Constructs instances of a grain class using constructor dependency injection.
/// </summary>
public class GrainConstructorArgumentFactory
{
private static readonly Type FacetMarkerInterfaceType = typeof(IFacetMetadata);
private static readonly MethodInfo GetFactoryMethod = typeof(GrainConstructorArgumentFactory).GetMethod(nameof(GetArgumentFactory), BindingFlags.NonPublic | BindingFlags.Static);
private readonly List<Factory<IGrainContext, object>> _argumentFactories;

/// <summary>
/// Initializes a new <see cref="GrainConstructorArgumentFactory"/> instance.
/// </summary>
/// <param name="serviceProvider">The service provider.</param>
/// <param name="grainType">The grain type.</param>
public GrainConstructorArgumentFactory(IServiceProvider serviceProvider, Type grainType)
{
_argumentFactories = new List<Factory<IGrainContext, object>>();

// Find the constructor - supports only single public constructor.
var parameters = grainType.GetConstructors().FirstOrDefault()?.GetParameters() ?? Enumerable.Empty<ParameterInfo>();
var types = new List<Type>();
foreach (var parameter in parameters)
{
// Look for attribute with a facet marker interface - supports only single facet attribute
var attribute = parameter.GetCustomAttributes()
.FirstOrDefault(static attribute => FacetMarkerInterfaceType.IsInstanceOfType(attribute));

if (attribute is null) continue;

// Since the IAttributeToFactoryMapper is specific to the attribute specialization, we create a generic method to provide a attribute independent call pattern.
var getFactory = GetFactoryMethod.MakeGenericMethod(attribute.GetType());
var argumentFactory = (Factory<IGrainContext, object>)getFactory.Invoke(this, new object[] { serviceProvider, parameter, attribute, grainType });

// Record the argument factory
_argumentFactories.Add(argumentFactory);

// Record the argument type
types.Add(parameter.ParameterType);
}

ArgumentTypes = types.ToArray();
}

/// <summary>
/// Gets the constructor argument types.
/// </summary>
public Type[] ArgumentTypes { get; }

/// <summary>
/// Creates the arguments for the grain constructor.
/// </summary>
/// <param name="grainContext">The grain context.</param>
/// <returns>The constructor arguments.</returns>
public object[] CreateArguments(IGrainContext grainContext)
{
var i = 0;
var results = new object[_argumentFactories.Count];
foreach (var argumentFactory in _argumentFactories)
{
results[i++] = argumentFactory(grainContext);
}

return results;
}

private static Factory<IGrainContext, object> GetArgumentFactory<TMetadata>(IServiceProvider services, ParameterInfo parameter, IFacetMetadata metadata, Type type)
where TMetadata : IFacetMetadata
{
var factoryMapper = services.GetService<IAttributeToFactoryMapper<TMetadata>>();
if (factoryMapper is null)
{
throw new OrleansException($"Missing attribute mapper for attribute {metadata.GetType()} used in grain constructor for grain type {type}.");
}

var factory = factoryMapper.GetFactory(parameter, (TMetadata)metadata);
if (factory is null)
{
throw new OrleansException($"Attribute mapper {factoryMapper.GetType()} failed to create a factory for grain type {type}.");
}

return factory;
}
}
}

0 comments on commit f0229bc

Please sign in to comment.