Move unused forked code to external directory (#1192)

* Move unused forked code to external directory

* Fix SLN build errors

* Add back resource provider core since it's referenced by main resource provider project

* Update PackageProjects step of pipeline
This commit is contained in:
Karl Burtram
2021-04-16 15:33:35 -07:00
committed by GitHub
parent dc6555a823
commit ccf95aed77
229 changed files with 10058 additions and 10124 deletions

View File

@@ -0,0 +1,44 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System;
using System.Composition;
namespace Microsoft.SqlTools.Hosting.Extensibility
{
/// <summary>
/// Base attribute class for all export definitions.
/// </summary>
[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class)]
public abstract class ExportStandardMetadataAttribute : ExportAttribute, IStandardMetadata
{
/// <summary>
/// Base class for DAC extensibility exports
/// </summary>
protected ExportStandardMetadataAttribute(Type contractType, string id, string displayName = null)
: base(contractType)
{
Id = id;
DisplayName = displayName;
}
/// <summary>
/// The version of this extension
/// </summary>
public string Version { get; set; }
/// <summary>
/// The id of the extension
/// </summary>
public string Id { get; }
/// <summary>
/// The display name for the extension
/// </summary>
public virtual string DisplayName { get; }
}
}

View File

@@ -0,0 +1,170 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Composition.Convention;
using System.Composition.Hosting;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Loader;
using Microsoft.SqlTools.Hosting.Utility;
namespace Microsoft.SqlTools.Hosting.Extensibility
{
/// <summary>
/// A MEF-based service provider. Supports any MEF-based configuration but is optimized for
/// service discovery over a set of DLLs in an application scope. Any service registering using
/// the <c>[Export(IServiceContract)]</c> attribute will be discovered and used by this service
/// provider if it's in the set of Assemblies / Types specified during its construction. Manual
/// override of this is supported by calling
/// <see cref="RegisteredServiceProvider.RegisterSingleService" /> and similar methods, since
/// this will initialize that service contract and avoid the MEF-based search and discovery
/// process. This allows the service provider to link into existing singleton / known services
/// while using MEF-based dependency injection and inversion of control for most of the code.
/// </summary>
public class ExtensionServiceProvider : RegisteredServiceProvider
{
private Func<ConventionBuilder, ContainerConfiguration> config;
public ExtensionServiceProvider(Func<ConventionBuilder, ContainerConfiguration> config)
{
Validate.IsNotNull(nameof(config), config);
this.config = config;
}
/// <summary>
/// Creates a service provider by loading a set of named assemblies, expected to be <paramref name="directory"/>
/// </summary>
/// <param name="directory">Directory to search for included assemblies</param>
/// <param name="inclusionList">full DLL names, case insensitive, of assemblies to include</param>
/// <returns><see cref="ExtensionServiceProvider"/> instance</returns>
public static ExtensionServiceProvider CreateFromAssembliesInDirectory(string directory, IList<string> inclusionList)
{
//AssemblyLoadContext context = new AssemblyLoader(directory);
var assemblyPaths = Directory.GetFiles(directory, "*.dll", SearchOption.TopDirectoryOnly);
List<Assembly> assemblies = new List<Assembly>();
foreach (var path in assemblyPaths)
{
// skip DLL files not in inclusion list
bool isInList = false;
foreach (var item in inclusionList)
{
if (path.EndsWith(item, StringComparison.OrdinalIgnoreCase))
{
isInList = true;
break;
}
}
if (!isInList)
{
continue;
}
try
{
assemblies.Add(AssemblyLoadContext.Default.LoadFromAssemblyPath(path));
}
catch (Exception)
{
// we expect exceptions trying to scan all DLLs since directory contains native libraries
}
}
return Create(assemblies);
}
public static ExtensionServiceProvider Create(IEnumerable<Assembly> assemblies)
{
Validate.IsNotNull(nameof(assemblies), assemblies);
return new ExtensionServiceProvider(conventions => new ContainerConfiguration().WithAssemblies(assemblies, conventions));
}
public static ExtensionServiceProvider Create(IEnumerable<Type> types)
{
Validate.IsNotNull(nameof(types), types);
return new ExtensionServiceProvider(conventions => new ContainerConfiguration().WithParts(types, conventions));
}
protected override IEnumerable<T> GetServicesImpl<T>()
{
EnsureExtensionStoreRegistered<T>();
return base.GetServicesImpl<T>();
}
private void EnsureExtensionStoreRegistered<T>()
{
if (!services.ContainsKey(typeof(T)))
{
ExtensionStore store = new ExtensionStore(typeof(T), config);
Register(() => store.GetExports<T>());
}
}
/// <summary>
/// Merges in new assemblies to the existing container configuration.
/// </summary>
public void AddAssembliesToConfiguration(IEnumerable<Assembly> assemblies)
{
Validate.IsNotNull(nameof(assemblies), assemblies);
var previousConfig = config;
this.config = conventions => {
// Chain in the existing configuration function's result, then include additional
// assemblies
ContainerConfiguration containerConfig = previousConfig(conventions);
return containerConfig.WithAssemblies(assemblies, conventions);
};
}
}
/// <summary>
/// A store for MEF exports of a specific type. Provides basic wrapper functionality around MEF to standarize how
/// we lookup types and return to callers.
/// </summary>
public class ExtensionStore
{
private readonly CompositionHost host;
private IList exports;
private readonly Type contractType;
/// <summary>
/// Initializes the store with a type to lookup exports of, and a function that configures the
/// lookup parameters.
/// </summary>
/// <param name="contractType">Type to use as a base for all extensions being looked up</param>
/// <param name="configure">Function that returns the configuration to be used</param>
public ExtensionStore(Type contractType, Func<ConventionBuilder, ContainerConfiguration> configure)
{
Validate.IsNotNull(nameof(contractType), contractType);
Validate.IsNotNull(nameof(configure), configure);
this.contractType = contractType;
ConventionBuilder builder = GetExportBuilder();
ContainerConfiguration config = configure(builder);
host = config.CreateContainer();
}
public IEnumerable<T> GetExports<T>()
{
if (exports == null)
{
exports = host.GetExports(contractType).ToList();
}
return exports.Cast<T>();
}
private ConventionBuilder GetExportBuilder()
{
// Define exports as matching a parent type, export as that parent type
var builder = new ConventionBuilder();
builder.ForTypesDerivedFrom(contractType).Export(exportConventionBuilder => exportConventionBuilder.AsContractType(contractType));
return builder;
}
}
}

View File

@@ -0,0 +1,21 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
namespace Microsoft.SqlTools.Hosting.Extensibility
{
/// <summary>
/// A Service that expects to lookup other services. Using this interface on an exported service
/// will ensure the <see cref="SetServiceProvider(IMultiServiceProvider)"/> method is called during
/// service initialization
/// </summary>
public interface IComposableService
{
/// <summary>
/// Supports settings the service provider being used to initialize the service.
/// This is useful to look up other services and use them in your own service.
/// </summary>
void SetServiceProvider(IMultiServiceProvider provider);
}
}

View File

@@ -0,0 +1,107 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System;
using System.Diagnostics;
using System.Threading.Tasks;
using Microsoft.SqlTools.Hosting.Protocol;
using Microsoft.SqlTools.Hosting.Utility;
namespace Microsoft.SqlTools.Hosting.Extensibility
{
/// <summary>
/// Defines a hosted service that communicates with external processes via
/// messages passed over the <see cref="ServiceHost"/>. The service defines
/// a standard initialization method where it can hook up to the host.
/// </summary>
public interface IHostedService
{
/// <summary>
/// Callback to initialize this service
/// </summary>
/// <param name="serviceHost"><see cref="IServiceHost"/> which supports registering
/// event handlers and other callbacks for messages passed to external callers</param>
void InitializeService(IServiceHost serviceHost);
/// <summary>
/// What is the service type that you wish to register?
/// </summary>
Type ServiceType { get; }
}
/// <summary>
/// Base class for <see cref="IHostedService"/> implementations that handles defining the <see cref="ServiceType"/>
/// being registered. This simplifies service registration. This also implements <see cref="IComposableService"/> which
/// allows injection of the service provider for lookup of other services.
///
/// Extending classes should implement per below code example
/// <code>
/// [Export(typeof(IHostedService)]
/// MyService : HostedService&lt;MyService&gt;
/// {
/// public override void InitializeService(IServiceHost serviceHost)
/// {
/// serviceHost.SetRequestHandler(MyRequest.Type, HandleMyRequest);
/// }
/// }
/// </code>
/// </summary>
/// <typeparam name="T">Type to be registered for lookup in the service provider</typeparam>
public abstract class HostedService<T> : IHostedService, IComposableService
{
protected IMultiServiceProvider ServiceProvider { get; private set; }
public virtual void SetServiceProvider(IMultiServiceProvider provider)
{
ServiceProvider = provider;
}
public Type ServiceType => typeof(T);
protected async Task<THandler> HandleRequestAsync<THandler>(
Func<Task<THandler>> handler,
RequestContext<THandler> requestContext,
string requestType)
{
Logger.Write(TraceEventType.Verbose, requestType);
try
{
THandler result = await handler();
requestContext.SendResult(result);
return result;
}
catch (Exception ex)
{
requestContext.SendError(ex.ToString());
}
return default(THandler);
}
protected async Task<THandler> HandleSyncRequestAsAsync<THandler>(
Func<THandler> handler,
RequestContext<THandler> requestContext,
string requestType)
{
Logger.Write(TraceEventType.Verbose, requestType);
return await Task.Factory.StartNew(() => {
try
{
THandler result = handler();
requestContext.SendResult(result);
return result;
}
catch (Exception ex)
{
requestContext.SendError(ex.ToString());
}
return default(THandler);
});
}
public abstract void InitializeService(IServiceHost serviceHost);
}
}

View File

@@ -0,0 +1,101 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.SqlTools.Hosting.Utility;
namespace Microsoft.SqlTools.Hosting.Extensibility
{
public interface IMultiServiceProvider
{
/// <summary>
/// Gets a service of a specific type. It is expected that only 1 instance of this type will be
/// available
/// </summary>
/// <typeparam name="T">Type of service to be found</typeparam>
/// <returns>Instance of T or null if not found</returns>
/// <exception cref="InvalidOperationException">The input sequence contains more than one element.-or-The input sequence is empty.</exception>
T GetService<T>();
/// <summary>
/// Gets a service of a specific type. The first service matching the specified filter will be returned
/// available
/// </summary>
/// <typeparam name="T">Type of service to be found</typeparam>
/// <param name="filter">Filter to use in </param>
/// <returns>Instance of T or null if not found</returns>
/// <exception cref="InvalidOperationException">The input sequence contains more than one element.-or-The input sequence is empty.</exception>
T GetService<T>(Predicate<T> filter);
/// <summary>
/// Gets multiple services of a given type
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns>An enumerable of matching services</returns>
IEnumerable<T> GetServices<T>();
/// <summary>
/// Gets multiple services of a given type, where they match a filter
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="filter"></param>
/// <returns></returns>
IEnumerable<T> GetServices<T>(Predicate<T> filter);
}
public abstract class ServiceProviderBase : IMultiServiceProvider
{
public T GetService<T>()
{
return GetServices<T>().SingleOrDefault();
}
public T GetService<T>(Predicate<T> filter)
{
Validate.IsNotNull(nameof(filter), filter);
return GetServices<T>().SingleOrDefault(t => filter(t));
}
public IEnumerable<T> GetServices<T>(Predicate<T> filter)
{
Validate.IsNotNull(nameof(filter), filter);
return GetServices<T>().Where(t => filter(t));
}
public virtual IEnumerable<T> GetServices<T>()
{
var services = GetServicesImpl<T>();
if (services == null)
{
return Enumerable.Empty<T>();
}
return services.Select(t =>
{
InitComposableService(t);
return t;
});
}
private void InitComposableService<T>(T t)
{
IComposableService c = t as IComposableService;
c?.SetServiceProvider(this);
}
/// <summary>
/// Gets all services using the build in implementation
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
protected abstract IEnumerable<T> GetServicesImpl<T>();
}
}

View File

@@ -0,0 +1,28 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
namespace Microsoft.SqlTools.Hosting.Extensibility
{
/// <summary>
/// Standard Metadata needed for extensions.
/// </summary>
public interface IStandardMetadata
{
/// <summary>
/// Extension version. Should be in the format "1.0.0.0" or similar
/// </summary>
string Version { get; }
/// <summary>
/// Unique Id used to identify the export.
/// </summary>
string Id { get; }
/// <summary>
/// Optional Display name describing the export type
/// </summary>
string DisplayName { get; }
}
}

View File

@@ -0,0 +1,121 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Microsoft.SqlTools.Hosting.Utility;
using Microsoft.SqlTools.Hosting.v2;
namespace Microsoft.SqlTools.Hosting.Extensibility
{
/// <summary>
/// A service provider implementation that allows registering of specific services
/// </summary>
public class RegisteredServiceProvider : ServiceProviderBase
{
public delegate IEnumerable ServiceLookup();
protected readonly Dictionary<Type, ServiceLookup> services = new Dictionary<Type, ServiceLookup>();
/// <summary>
/// Registers a singular service to be returned during lookup
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns>this provider, to simplify fluent declarations</returns>
/// <exception cref="ArgumentNullException">If service is null</exception>
/// <exception cref="InvalidOperationException">If an existing service is already registered</exception>
public virtual RegisteredServiceProvider RegisterSingleService<T>(T service)
{
Validate.IsNotNull(nameof(service), service);
ThrowIfAlreadyRegistered<T>();
services.Add(typeof(T), () => service.AsSingleItemEnumerable());
return this;
}
/// <summary>
/// Registers a singular service to be returned during lookup
/// </summary>
/// <param name="type">
/// Type or interface this service should be registed as. Any <see cref="IMultiServiceProvider.GetServices{T}"/> request
/// for that type will return this service
/// </param>
/// <param name="service">service object to be added</param>
/// <returns>this provider, to simplify fluent declarations</returns>
/// <exception cref="ArgumentNullException">If service is null</exception>
/// <exception cref="InvalidOperationException">If an existing service is already registered</exception>
public virtual RegisteredServiceProvider RegisterSingleService(Type type, object service)
{
Validate.IsNotNull(nameof(type), type);
Validate.IsNotNull(nameof(service), service);
ThrowIfAlreadyRegistered(type);
ThrowIfIncompatible(type, service);
services.Add(type, service.AsSingleItemEnumerable);
return this;
}
/// <summary>
/// Registers a function that can look up multiple services
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns>this provider, to simplify fluent declarations</returns>
/// <exception cref="ArgumentNullException">If <paramref name="serviceLookup"/> is null</exception>
/// <exception cref="InvalidOperationException">If an existing service is already registered</exception>
public virtual RegisteredServiceProvider Register<T>(Func<IEnumerable<T>> serviceLookup)
{
Validate.IsNotNull(nameof(serviceLookup), serviceLookup);
ThrowIfAlreadyRegistered<T>();
services.Add(typeof(T), () => serviceLookup());
return this;
}
public virtual void RegisterHostedServices()
{
// Register all hosted services the service provider for their requested service type.
// This ensures that when searching for the ConnectionService (eg) you can get it
// without searching for an IHosted service of type ConnectionService
foreach (IHostedService service in GetServices<IHostedService>())
{
RegisterSingleService(service.ServiceType, service);
}
}
private void ThrowIfAlreadyRegistered<T>()
{
ThrowIfAlreadyRegistered(typeof(T));
}
private void ThrowIfAlreadyRegistered(Type type)
{
if (services.ContainsKey(type))
{
throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, SR.ServiceAlreadyRegistered, type.Name));
}
}
private void ThrowIfIncompatible(Type type, object service)
{
if (!type.IsInstanceOfType(service))
{
throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, SR.ServiceNotOfExpectedType, service.GetType().Name, type.Name));
}
}
protected override IEnumerable<T> GetServicesImpl<T>()
{
ServiceLookup serviceLookup;
if (services.TryGetValue(typeof(T), out serviceLookup))
{
return serviceLookup().Cast<T>();
}
return Enumerable.Empty<T>();
}
}
}