-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Refactor - setup per plug-in projects (#32)
* Refactor - split into multiple projects Signed-off-by: Victor Chang <[email protected]> * Update build pipeline Signed-off-by: Victor Chang <[email protected]> * Update unit test and READMEs Signed-off-by: Victor Chang <[email protected]> * Exclude test projects from package.sh Signed-off-by: Victor Chang <[email protected]>
- Loading branch information
Showing
38 changed files
with
931 additions
and
155 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -86,4 +86,4 @@ | |
</Parameters> | ||
</Rule> | ||
</Rules> | ||
</AnalysisInput> | ||
</AnalysisInput> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
// SPDX-FileCopyrightText: © 2022 MONAI Consortium | ||
// SPDX-License-Identifier: Apache License 2.0 | ||
|
||
using System.IO.Abstractions; | ||
using System.Reflection; | ||
using Ardalis.GuardClauses; | ||
using Microsoft.Extensions.DependencyInjection; | ||
using Monai.Deploy.Messaging.API; | ||
using Monai.Deploy.Messaging.Configuration; | ||
|
||
namespace Monai.Deploy.Messaging | ||
{ | ||
public static class IServiceCollectionExtensions | ||
{ | ||
private static IFileSystem? s_fileSystem; | ||
|
||
/// <summary> | ||
/// Configures all dependencies required for the MONAI Deploy Message Broker Subscriber Service. | ||
/// </summary> | ||
/// <param name="services">Instance of <see cref="IServiceCollection"/>.</param> | ||
/// <param name="fullyQualifiedTypeName">Fully qualified type name of the service to use.</param> | ||
/// <returns>Instance of <see cref="IServiceCollection"/>.</returns> | ||
/// <exception cref="ConfigurationException"></exception> | ||
public static IServiceCollection AddMonaiDeployMessageBrokerSubscriberService(this IServiceCollection services, string fullyQualifiedTypeName) | ||
=> AddMonaiDeployMessageBrokerSubscriberService(services, fullyQualifiedTypeName, new FileSystem()); | ||
|
||
/// <summary> | ||
/// Configures all dependencies required for the MONAI Deploy Message Broker Subscriber Service. | ||
/// </summary> | ||
/// <param name="services">Instance of <see cref="IServiceCollection"/>.</param> | ||
/// <param name="fullyQualifiedTypeName">Fully qualified type name of the service to use.</param> | ||
/// <param name="fileSystem">Instance of <see cref="IFileSystem"/>.</param> | ||
/// <returns>Instance of <see cref="IServiceCollection"/>.</returns> | ||
/// <exception cref="ConfigurationException"></exception> | ||
public static IServiceCollection AddMonaiDeployMessageBrokerSubscriberService(this IServiceCollection services, string fullyQualifiedTypeName, IFileSystem fileSystem) | ||
=> Add<IMessageBrokerSubscriberService, SubscriberServiceRegistrationBase>(services, fullyQualifiedTypeName, fileSystem); | ||
|
||
/// <summary> | ||
/// Configures all dependencies required for the MONAI Deploy Message Broker Publisher Service. | ||
/// </summary> | ||
/// <param name="services">Instance of <see cref="IServiceCollection"/>.</param> | ||
/// <param name="fullyQualifiedTypeName">Fully qualified type name of the service to use.</param> | ||
/// <returns>Instance of <see cref="IServiceCollection"/>.</returns> | ||
/// <exception cref="ConfigurationException"></exception> | ||
public static IServiceCollection AddMonaiDeployMessageBrokerPublisherService(this IServiceCollection services, string fullyQualifiedTypeName) | ||
=> AddMonaiDeployMessageBrokerPublisherService(services, fullyQualifiedTypeName, new FileSystem()); | ||
|
||
/// <summary> | ||
/// Configures all dependencies required for the MONAI Deploy Message Broker Publisher Service. | ||
/// </summary> | ||
/// <param name="services">Instance of <see cref="IServiceCollection"/>.</param> | ||
/// <param name="fullyQualifiedTypeName">Fully qualified type name of the service to use.</param> | ||
/// <param name="fileSystem">Instance of <see cref="IFileSystem"/>.</param> | ||
/// <returns>Instance of <see cref="IServiceCollection"/>.</returns> | ||
/// <exception cref="ConfigurationException"></exception> | ||
public static IServiceCollection AddMonaiDeployMessageBrokerPublisherService(this IServiceCollection services, string fullyQualifiedTypeName, IFileSystem fileSystem) | ||
=> Add<IMessageBrokerPublisherService, PublisherServiceRegistrationBase>(services, fullyQualifiedTypeName, fileSystem); | ||
|
||
private static IServiceCollection Add<T, U>(this IServiceCollection services, string fullyQualifiedTypeName, IFileSystem fileSystem) where U : ServiceRegistrationBase | ||
{ | ||
Guard.Against.NullOrWhiteSpace(fullyQualifiedTypeName, nameof(fullyQualifiedTypeName)); | ||
Guard.Against.Null(fileSystem, nameof(fileSystem)); | ||
|
||
s_fileSystem = fileSystem; | ||
|
||
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve; | ||
|
||
try | ||
{ | ||
var serviceAssembly = LoadAssemblyFromDisk(GetAssemblyName(fullyQualifiedTypeName)); | ||
var serviceRegistrationType = serviceAssembly.GetTypes().FirstOrDefault(p => p.BaseType == typeof(U)); | ||
|
||
if (serviceRegistrationType is null || Activator.CreateInstance(serviceRegistrationType, fullyQualifiedTypeName) is not U serviceRegistrar) | ||
{ | ||
throw new ConfigurationException($"Service registrar cannot be found for the configured plug-in '{fullyQualifiedTypeName}'."); | ||
} | ||
|
||
if (!IsSupportedType<T>(fullyQualifiedTypeName, serviceAssembly)) | ||
{ | ||
throw new ConfigurationException($"The configured type '{fullyQualifiedTypeName}' does not implement the {typeof(T).Name} interface."); | ||
} | ||
|
||
return serviceRegistrar.Configure(services); | ||
} | ||
finally | ||
{ | ||
AppDomain.CurrentDomain.AssemblyResolve -= CurrentDomain_AssemblyResolve; | ||
} | ||
} | ||
|
||
private static bool IsSupportedType<T>(string fullyQualifiedTypeName, Assembly storageServiceAssembly) | ||
{ | ||
Guard.Against.NullOrWhiteSpace(fullyQualifiedTypeName, nameof(fullyQualifiedTypeName)); | ||
Guard.Against.Null(storageServiceAssembly, nameof(storageServiceAssembly)); | ||
|
||
var storageServiceType = Type.GetType(fullyQualifiedTypeName, assemblyeName => storageServiceAssembly, null, false); | ||
|
||
return storageServiceType is not null && | ||
storageServiceType.GetInterfaces().Contains(typeof(T)); | ||
} | ||
|
||
private static string GetAssemblyName(string fullyQualifiedTypeName) | ||
{ | ||
var assemblyNameParts = fullyQualifiedTypeName.Split(',', StringSplitOptions.None); | ||
if (assemblyNameParts.Length < 2 || string.IsNullOrWhiteSpace(assemblyNameParts[1])) | ||
{ | ||
throw new ConfigurationException($"The configured service type '{fullyQualifiedTypeName}' is not a valid fully qualified type name. E.g. {MessageBrokerServiceConfiguration.DefaultPublisherAssemblyName}") | ||
{ | ||
HelpLink = "https://docs.microsoft.com/en-us/dotnet/standard/assembly/find-fully-qualified-name" | ||
}; | ||
} | ||
|
||
return assemblyNameParts[1].Trim(); | ||
} | ||
|
||
private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) | ||
{ | ||
Guard.Against.Null(args, nameof(args)); | ||
|
||
var requestedAssemblyName = new AssemblyName(args.Name); | ||
return LoadAssemblyFromDisk(requestedAssemblyName.Name); | ||
} | ||
|
||
private static Assembly LoadAssemblyFromDisk(string assemblyName) | ||
{ | ||
Guard.Against.NullOrWhiteSpace(assemblyName, nameof(assemblyName)); | ||
Guard.Against.Null(s_fileSystem, nameof(s_fileSystem)); | ||
|
||
if (!s_fileSystem.Directory.Exists(SR.PlugInDirectoryPath)) | ||
{ | ||
throw new ConfigurationException($"Plug-in directory '{SR.PlugInDirectoryPath}' cannot be found."); | ||
} | ||
|
||
var assemblyFilePath = s_fileSystem.Path.Combine(SR.PlugInDirectoryPath, $"{assemblyName}.dll"); | ||
if (!s_fileSystem.File.Exists(assemblyFilePath)) | ||
{ | ||
throw new ConfigurationException($"The configured plug-in '{assemblyFilePath}' cannot be found."); | ||
} | ||
|
||
var asesmblyeData = s_fileSystem.File.ReadAllBytes(assemblyFilePath); | ||
return Assembly.Load(asesmblyeData); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
using System.Runtime.CompilerServices; | ||
|
||
[assembly: InternalsVisibleTo("Monai.Deploy.Messaging.Test")] | ||
[assembly: InternalsVisibleTo("Monai.Deploy.Messaging.Tests")] | ||
[assembly: InternalsVisibleTo("Monai.Deploy.Messaging.RabbitMQ.Tests")] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
// SPDX-FileCopyrightText: © 2022 MONAI Consortium | ||
// SPDX-License-Identifier: Apache License 2.0 | ||
|
||
namespace Monai.Deploy.Messaging | ||
{ | ||
internal static class SR | ||
{ | ||
public const string PlugInDirectoryName = "plug-ins"; | ||
public static readonly string PlugInDirectoryPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, SR.PlugInDirectoryName); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
// SPDX-FileCopyrightText: © 2022 MONAI Consortium | ||
// SPDX-License-Identifier: Apache License 2.0 | ||
|
||
using Ardalis.GuardClauses; | ||
using Microsoft.Extensions.DependencyInjection; | ||
using Monai.Deploy.Messaging.Configuration; | ||
|
||
namespace Monai.Deploy.Messaging | ||
{ | ||
public abstract class SubscriberServiceRegistrationBase : ServiceRegistrationBase | ||
{ | ||
protected SubscriberServiceRegistrationBase(string fullyQualifiedAssemblyName) : base(fullyQualifiedAssemblyName) | ||
{ | ||
} | ||
} | ||
|
||
public abstract class PublisherServiceRegistrationBase : ServiceRegistrationBase | ||
{ | ||
protected PublisherServiceRegistrationBase(string fullyQualifiedAssemblyName) : base(fullyQualifiedAssemblyName) | ||
{ | ||
} | ||
} | ||
|
||
public abstract class ServiceRegistrationBase | ||
{ | ||
protected string FullyQualifiedAssemblyName { get; } | ||
protected string AssemblyFilename { get; } | ||
|
||
protected ServiceRegistrationBase(string fullyQualifiedAssemblyName) | ||
{ | ||
Guard.Against.NullOrWhiteSpace(fullyQualifiedAssemblyName, nameof(fullyQualifiedAssemblyName)); | ||
FullyQualifiedAssemblyName = fullyQualifiedAssemblyName; | ||
AssemblyFilename = ParseAssemblyName(); | ||
} | ||
|
||
private string ParseAssemblyName() | ||
{ | ||
var assemblyNameParts = FullyQualifiedAssemblyName.Split(',', StringSplitOptions.None); | ||
if (assemblyNameParts.Length < 2 || string.IsNullOrWhiteSpace(assemblyNameParts[1])) | ||
{ | ||
throw new ConfigurationException($"Storage service '{FullyQualifiedAssemblyName}' is invalid. Please provide a fully qualified name.") | ||
{ | ||
HelpLink = "https://docs.microsoft.com/en-us/dotnet/standard/assembly/find-fully-qualified-name" | ||
}; | ||
} | ||
|
||
return assemblyNameParts[1].Trim(); | ||
} | ||
|
||
public abstract IServiceCollection Configure(IServiceCollection services); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.