mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-02-16 18:47:57 -05:00
Add v2 of the Hosting Service and build nuget packages for it (#675)
* Port v2 of Hosting service to SqlToolsService - Renamed project to .v2 so that existing hosted service isn't impacted - Copied over the CoreServices project which contains ConnectionServiceCore and other reusable services for anything interacting with MSSQL - Ported unit test project across and verified tests run. * Nuget package support for reusable DLLs * Use 1.1 version per Karl's suggestion * Use correct license URL and project URL * Use new SMO packages
This commit is contained in:
210
src/Microsoft.SqlTools.Hosting.v2/ServiceHost.cs
Normal file
210
src/Microsoft.SqlTools.Hosting.v2/ServiceHost.cs
Normal file
@@ -0,0 +1,210 @@
|
||||
//
|
||||
// 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 System.Threading.Tasks;
|
||||
using Microsoft.SqlTools.DataProtocol.Contracts;
|
||||
using Microsoft.SqlTools.DataProtocol.Contracts.ServerCapabilities;
|
||||
using Microsoft.SqlTools.Hosting.Channels;
|
||||
using Microsoft.SqlTools.Hosting.Contracts;
|
||||
using Microsoft.SqlTools.Hosting.Contracts.Internal;
|
||||
using Microsoft.SqlTools.Hosting.Protocol;
|
||||
using Microsoft.SqlTools.Hosting.Utility;
|
||||
|
||||
namespace Microsoft.SqlTools.Hosting
|
||||
{
|
||||
public class ServiceHost : IServiceHost
|
||||
{
|
||||
private const int DefaultShutdownTimeoutSeconds = 120;
|
||||
|
||||
#region Fields
|
||||
|
||||
private int? shutdownTimeoutSeconds;
|
||||
|
||||
internal readonly List<Func<InitializeParameters, IEventSender, Task>> initCallbacks;
|
||||
internal readonly List<Func<object, IEventSender, Task>> shutdownCallbacks;
|
||||
internal IJsonRpcHost jsonRpcHost;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Construction
|
||||
|
||||
/// <summary>
|
||||
/// Base constructor
|
||||
/// </summary>
|
||||
internal ServiceHost()
|
||||
{
|
||||
shutdownCallbacks = new List<Func<object, IEventSender, Task>>();
|
||||
initCallbacks = new List<Func<InitializeParameters, IEventSender, Task>>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new service host that with ability to provide custom protocol channels
|
||||
/// </summary>
|
||||
/// <param name="protocolChannel">Channel to use for JSON RPC input/output</param>
|
||||
public ServiceHost(ChannelBase protocolChannel)
|
||||
: this()
|
||||
{
|
||||
Validate.IsNotNull(nameof(protocolChannel), protocolChannel);
|
||||
jsonRpcHost = new JsonRpcHost(protocolChannel);
|
||||
|
||||
// Register any request that the service host will handle
|
||||
SetEventHandler(ExitNotification.Type, HandleExitNotification, true);
|
||||
SetAsyncRequestHandler(ShutdownRequest.Type, HandleShutdownRequest, true);
|
||||
SetAsyncRequestHandler(InitializeRequest.Type, HandleInitializeRequest, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new service host intended to be used as a JSON RPC server. StdIn is used
|
||||
/// for receiving messages, StdOut is used for sending messages.
|
||||
/// </summary>
|
||||
/// <returns>Service host as a JSON RPC server over StdI/O</returns>
|
||||
public static ServiceHost CreateDefaultServer()
|
||||
{
|
||||
return new ServiceHost(new StdioServerChannel());
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
public int ShutdownTimeoutSeconds
|
||||
{
|
||||
get => shutdownTimeoutSeconds ?? DefaultShutdownTimeoutSeconds;
|
||||
set => shutdownTimeoutSeconds = value;
|
||||
}
|
||||
|
||||
public InitializeResponse InitializeResponse { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region IServiceHost Implementations
|
||||
|
||||
public void RegisterShutdownTask(Func<object, IEventSender, Task> shutdownCallback)
|
||||
{
|
||||
Validate.IsNotNull(nameof(shutdownCallback), shutdownCallback);
|
||||
shutdownCallbacks.Add(shutdownCallback);
|
||||
}
|
||||
|
||||
public void RegisterInitializeTask(Func<InitializeParameters, IEventSender, Task> initializeCallback)
|
||||
{
|
||||
Validate.IsNotNull(nameof(initializeCallback), initializeCallback);
|
||||
initCallbacks.Add(initializeCallback);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IJsonRpcHost Implementation
|
||||
|
||||
public void SendEvent<TParams>(
|
||||
EventType<TParams> eventType,
|
||||
TParams eventParams)
|
||||
{
|
||||
jsonRpcHost.SendEvent(eventType, eventParams);
|
||||
}
|
||||
|
||||
public Task<TResult> SendRequest<TParams, TResult>(
|
||||
RequestType<TParams, TResult> requestType,
|
||||
TParams requestParams)
|
||||
{
|
||||
return jsonRpcHost.SendRequest(requestType, requestParams);
|
||||
}
|
||||
|
||||
public void SetAsyncEventHandler<TParams>(
|
||||
EventType<TParams> eventType,
|
||||
Func<TParams, EventContext, Task> eventHandler,
|
||||
bool overrideExisting)
|
||||
{
|
||||
jsonRpcHost.SetAsyncEventHandler(eventType, eventHandler, overrideExisting);
|
||||
}
|
||||
|
||||
public void SetEventHandler<TParams>(
|
||||
EventType<TParams> eventType,
|
||||
Action<TParams, EventContext> eventHandler,
|
||||
bool overrideExisting)
|
||||
{
|
||||
jsonRpcHost.SetEventHandler(eventType, eventHandler, overrideExisting);
|
||||
}
|
||||
|
||||
public void SetAsyncRequestHandler<TParams, TResult>(
|
||||
RequestType<TParams, TResult> requestType,
|
||||
Func<TParams, RequestContext<TResult>, Task> requestHandler,
|
||||
bool overrideExisting)
|
||||
{
|
||||
jsonRpcHost.SetAsyncRequestHandler(requestType, requestHandler, overrideExisting);
|
||||
}
|
||||
|
||||
public void SetRequestHandler<TParams, TResult>(
|
||||
RequestType<TParams, TResult> requestType,
|
||||
Action<TParams, RequestContext<TResult>> requestHandler,
|
||||
bool overrideExisting)
|
||||
{
|
||||
jsonRpcHost.SetRequestHandler(requestType, requestHandler, overrideExisting);
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
// Start the host
|
||||
jsonRpcHost.Start();
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
jsonRpcHost.Stop();
|
||||
}
|
||||
|
||||
public void WaitForExit()
|
||||
{
|
||||
jsonRpcHost.WaitForExit();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Request Handlers
|
||||
|
||||
internal async Task HandleInitializeRequest(InitializeParameters initParams, RequestContext<InitializeResponse> requestContext)
|
||||
{
|
||||
Logger.Instance.Write(LogLevel.Normal, "Service host received intialize request");
|
||||
|
||||
// Call all initialize methods provided by the service components
|
||||
IEnumerable<Task> initializeTasks = initCallbacks.Select(t => t(initParams, requestContext));
|
||||
|
||||
// Respond to initialize once all tasks are completed
|
||||
await Task.WhenAll(initializeTasks);
|
||||
|
||||
if (InitializeResponse == null)
|
||||
{
|
||||
InitializeResponse = new InitializeResponse
|
||||
{
|
||||
Capabilities = new ServerCapabilities()
|
||||
};
|
||||
}
|
||||
requestContext.SendResult(InitializeResponse);
|
||||
}
|
||||
|
||||
internal void HandleExitNotification(object exitParams, EventContext eventContext)
|
||||
{
|
||||
// Stop the server channel
|
||||
Stop();
|
||||
}
|
||||
|
||||
internal async Task HandleShutdownRequest(object shutdownParams, RequestContext<object> requestContext)
|
||||
{
|
||||
Logger.Instance.Write(LogLevel.Normal, "Service host received shutdown request");
|
||||
|
||||
// Call all the shutdown methods provided by the service components
|
||||
IEnumerable<Task> shutdownTasks = shutdownCallbacks.Select(t => t(shutdownParams, requestContext));
|
||||
|
||||
// Shutdown once all tasks are completed, or after the timeout expires, whichever comes first
|
||||
TimeSpan shutdownTimeout = TimeSpan.FromSeconds(ShutdownTimeoutSeconds);
|
||||
await Task.WhenAny(Task.WhenAll(shutdownTasks), Task.Delay(shutdownTimeout));
|
||||
requestContext.SendResult(null);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user