fixed couple of issues in task service (#377)

This commit is contained in:
Leila Lali
2017-06-12 15:52:49 -07:00
committed by GitHub
parent 869cd1439f
commit 58f438176b
7 changed files with 259 additions and 246 deletions

View File

@@ -1,229 +1,229 @@
// //
// Copyright (c) Microsoft. All rights reserved. // Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information. // Licensed under the MIT license. See LICENSE file in the project root for full license information.
// //
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.SqlTools.Extensibility; using Microsoft.SqlTools.Extensibility;
using Microsoft.SqlTools.Hosting; using Microsoft.SqlTools.Hosting;
using Microsoft.SqlTools.Hosting.Contracts; using Microsoft.SqlTools.Hosting.Contracts;
using Microsoft.SqlTools.Hosting.Protocol; using Microsoft.SqlTools.Hosting.Protocol;
using Microsoft.SqlTools.Hosting.Protocol.Channel; using Microsoft.SqlTools.Hosting.Protocol.Channel;
using Microsoft.SqlTools.Utility; using Microsoft.SqlTools.Utility;
using Microsoft.SqlTools.ServiceLayer.Connection; using Microsoft.SqlTools.ServiceLayer.Connection;
using Microsoft.SqlTools.ServiceLayer.Admin; using Microsoft.SqlTools.ServiceLayer.Admin;
namespace Microsoft.SqlTools.ServiceLayer.Hosting namespace Microsoft.SqlTools.ServiceLayer.Hosting
{ {
/// <summary> /// <summary>
/// SQL Tools VS Code Language Server request handler. Provides the entire JSON RPC /// SQL Tools VS Code Language Server request handler. Provides the entire JSON RPC
/// implementation for sending/receiving JSON requests and dispatching the requests to /// implementation for sending/receiving JSON requests and dispatching the requests to
/// handlers that are registered prior to startup. /// handlers that are registered prior to startup.
/// </summary> /// </summary>
public sealed class ServiceHost : ServiceHostBase public sealed class ServiceHost : ServiceHostBase
{ {
public const string ProviderName = "MSSQL"; public const string ProviderName = "MSSQL";
private const string ProviderDescription = "Microsoft SQL Server"; private const string ProviderDescription = "Microsoft SQL Server";
private const string ProviderProtocolVersion = "1.0"; private const string ProviderProtocolVersion = "1.0";
/// <summary> /// <summary>
/// This timeout limits the amount of time that shutdown tasks can take to complete /// This timeout limits the amount of time that shutdown tasks can take to complete
/// prior to the process shutting down. /// prior to the process shutting down.
/// </summary> /// </summary>
private const int ShutdownTimeoutInSeconds = 120; private const int ShutdownTimeoutInSeconds = 120;
public static readonly string[] CompletionTriggerCharacters = new string[] { ".", "-", ":", "\\", "[", "\"" }; public static readonly string[] CompletionTriggerCharacters = new string[] { ".", "-", ":", "\\", "[", "\"" };
private IMultiServiceProvider serviceProvider; private IMultiServiceProvider serviceProvider;
#region Singleton Instance Code #region Singleton Instance Code
/// <summary> /// <summary>
/// Singleton instance of the service host for internal storage /// Singleton instance of the service host for internal storage
/// </summary> /// </summary>
private static readonly Lazy<ServiceHost> instance = new Lazy<ServiceHost>(() => new ServiceHost()); private static readonly Lazy<ServiceHost> instance = new Lazy<ServiceHost>(() => new ServiceHost());
/// <summary> /// <summary>
/// Current instance of the ServiceHost /// Current instance of the ServiceHost
/// </summary> /// </summary>
public static ServiceHost Instance public static ServiceHost Instance
{ {
get { return instance.Value; } get { return instance.Value; }
} }
/// <summary> /// <summary>
/// Constructs new instance of ServiceHost using the host and profile details provided. /// Constructs new instance of ServiceHost using the host and profile details provided.
/// Access is private to ensure only one instance exists at a time. /// Access is private to ensure only one instance exists at a time.
/// </summary> /// </summary>
private ServiceHost() : base(new StdioServerChannel()) private ServiceHost() : base(new StdioServerChannel())
{ {
// Initialize the shutdown activities // Initialize the shutdown activities
shutdownCallbacks = new List<ShutdownCallback>(); shutdownCallbacks = new List<ShutdownCallback>();
initializeCallbacks = new List<InitializeCallback>(); initializeCallbacks = new List<InitializeCallback>();
} }
public IMultiServiceProvider ServiceProvider public IMultiServiceProvider ServiceProvider
{ {
get get
{ {
return serviceProvider; return serviceProvider;
} }
internal set internal set
{ {
serviceProvider = value; serviceProvider = value;
} }
} }
/// <summary> /// <summary>
/// Provide initialization that must occur after the service host is started /// Provide initialization that must occur after the service host is started
/// </summary> /// </summary>
public void InitializeRequestHandlers() public void InitializeRequestHandlers()
{ {
// Register the requests that this service host will handle // Register the requests that this service host will handle
this.SetRequestHandler(InitializeRequest.Type, HandleInitializeRequest); this.SetRequestHandler(InitializeRequest.Type, HandleInitializeRequest);
this.SetRequestHandler(CapabilitiesRequest.Type, HandleCapabilitiesRequest); this.SetRequestHandler(CapabilitiesRequest.Type, HandleCapabilitiesRequest);
this.SetRequestHandler(ShutdownRequest.Type, HandleShutdownRequest); this.SetRequestHandler(ShutdownRequest.Type, HandleShutdownRequest);
this.SetRequestHandler(VersionRequest.Type, HandleVersionRequest); this.SetRequestHandler(VersionRequest.Type, HandleVersionRequest);
} }
#endregion #endregion
#region Member Variables #region Member Variables
/// <summary> /// <summary>
/// Delegate definition for the host shutdown event /// Delegate definition for the host shutdown event
/// </summary> /// </summary>
/// <param name="shutdownParams"></param> /// <param name="shutdownParams"></param>
/// <param name="shutdownRequestContext"></param> /// <param name="shutdownRequestContext"></param>
public delegate Task ShutdownCallback(object shutdownParams, RequestContext<object> shutdownRequestContext); public delegate Task ShutdownCallback(object shutdownParams, RequestContext<object> shutdownRequestContext);
/// <summary> /// <summary>
/// Delegate definition for the host initialization event /// Delegate definition for the host initialization event
/// </summary> /// </summary>
/// <param name="startupParams"></param> /// <param name="startupParams"></param>
/// <param name="requestContext"></param> /// <param name="requestContext"></param>
public delegate Task InitializeCallback(InitializeRequest startupParams, RequestContext<InitializeResult> requestContext); public delegate Task InitializeCallback(InitializeRequest startupParams, RequestContext<InitializeResult> requestContext);
private readonly List<ShutdownCallback> shutdownCallbacks; private readonly List<ShutdownCallback> shutdownCallbacks;
private readonly List<InitializeCallback> initializeCallbacks; private readonly List<InitializeCallback> initializeCallbacks;
private static readonly Version serviceVersion = Assembly.GetEntryAssembly().GetName().Version; private static readonly Version serviceVersion = Assembly.GetEntryAssembly().GetName().Version;
#endregion #endregion
#region Public Methods #region Public Methods
/// <summary> /// <summary>
/// Adds a new callback to be called when the shutdown request is submitted /// Adds a new callback to be called when the shutdown request is submitted
/// </summary> /// </summary>
/// <param name="callback">Callback to perform when a shutdown request is submitted</param> /// <param name="callback">Callback to perform when a shutdown request is submitted</param>
public void RegisterShutdownTask(ShutdownCallback callback) public void RegisterShutdownTask(ShutdownCallback callback)
{ {
shutdownCallbacks.Add(callback); shutdownCallbacks.Add(callback);
} }
/// <summary> /// <summary>
/// Add a new method to be called when the initialize request is submitted /// Add a new method to be called when the initialize request is submitted
/// </summary> /// </summary>
/// <param name="callback">Callback to perform when an initialize request is submitted</param> /// <param name="callback">Callback to perform when an initialize request is submitted</param>
public void RegisterInitializeTask(InitializeCallback callback) public void RegisterInitializeTask(InitializeCallback callback)
{ {
initializeCallbacks.Add(callback); initializeCallbacks.Add(callback);
} }
#endregion #endregion
#region Request Handlers #region Request Handlers
/// <summary> /// <summary>
/// Handles the shutdown event for the Language Server /// Handles the shutdown event for the Language Server
/// </summary> /// </summary>
private async Task HandleShutdownRequest(object shutdownParams, RequestContext<object> requestContext) private async Task HandleShutdownRequest(object shutdownParams, RequestContext<object> requestContext)
{ {
Logger.Write(LogLevel.Normal, "Service host is shutting down..."); Logger.Write(LogLevel.Normal, "Service host is shutting down...");
// Call all the shutdown methods provided by the service components // Call all the shutdown methods provided by the service components
Task[] shutdownTasks = shutdownCallbacks.Select(t => t(shutdownParams, requestContext)).ToArray(); Task[] shutdownTasks = shutdownCallbacks.Select(t => t(shutdownParams, requestContext)).ToArray();
TimeSpan shutdownTimeout = TimeSpan.FromSeconds(ShutdownTimeoutInSeconds); TimeSpan shutdownTimeout = TimeSpan.FromSeconds(ShutdownTimeoutInSeconds);
// shut down once all tasks are completed, or after the timeout expires, whichever comes first. // shut down once all tasks are completed, or after the timeout expires, whichever comes first.
await Task.WhenAny(Task.WhenAll(shutdownTasks), Task.Delay(shutdownTimeout)).ContinueWith(t => Environment.Exit(0)); await Task.WhenAny(Task.WhenAll(shutdownTasks), Task.Delay(shutdownTimeout)).ContinueWith(t => Environment.Exit(0));
} }
/// <summary> /// <summary>
/// Handles the initialization request /// Handles the initialization request
/// </summary> /// </summary>
/// <param name="initializeParams"></param> /// <param name="initializeParams"></param>
/// <param name="requestContext"></param> /// <param name="requestContext"></param>
/// <returns></returns> /// <returns></returns>
internal async Task HandleInitializeRequest(InitializeRequest initializeParams, RequestContext<InitializeResult> requestContext) internal async Task HandleInitializeRequest(InitializeRequest initializeParams, RequestContext<InitializeResult> requestContext)
{ {
// Call all tasks that registered on the initialize request // Call all tasks that registered on the initialize request
var initializeTasks = initializeCallbacks.Select(t => t(initializeParams, requestContext)); var initializeTasks = initializeCallbacks.Select(t => t(initializeParams, requestContext));
await Task.WhenAll(initializeTasks); await Task.WhenAll(initializeTasks);
// TODO: Figure out where this needs to go to be agnostic of the language // TODO: Figure out where this needs to go to be agnostic of the language
// Send back what this server can do // Send back what this server can do
await requestContext.SendResult( await requestContext.SendResult(
new InitializeResult new InitializeResult
{ {
Capabilities = new ServerCapabilities Capabilities = new ServerCapabilities
{ {
TextDocumentSync = TextDocumentSyncKind.Incremental, TextDocumentSync = TextDocumentSyncKind.Incremental,
DefinitionProvider = true, DefinitionProvider = true,
ReferencesProvider = false, ReferencesProvider = false,
DocumentFormattingProvider = true, DocumentFormattingProvider = true,
DocumentRangeFormattingProvider = true, DocumentRangeFormattingProvider = true,
DocumentHighlightProvider = false, DocumentHighlightProvider = false,
HoverProvider = true, HoverProvider = true,
CompletionProvider = new CompletionOptions CompletionProvider = new CompletionOptions
{ {
ResolveProvider = true, ResolveProvider = true,
TriggerCharacters = CompletionTriggerCharacters TriggerCharacters = CompletionTriggerCharacters
}, },
SignatureHelpProvider = new SignatureHelpOptions SignatureHelpProvider = new SignatureHelpOptions
{ {
TriggerCharacters = new string[] { " ", "," } TriggerCharacters = new string[] { " ", "," }
} }
} }
}); });
} }
/// <summary> /// <summary>
/// Handles a request for the capabilities request /// Handles a request for the capabilities request
/// </summary> /// </summary>
internal async Task HandleCapabilitiesRequest( internal async Task HandleCapabilitiesRequest(
CapabilitiesRequest initializeParams, CapabilitiesRequest initializeParams,
RequestContext<CapabilitiesResult> requestContext) RequestContext<CapabilitiesResult> requestContext)
{ {
await requestContext.SendResult( await requestContext.SendResult(
new CapabilitiesResult new CapabilitiesResult
{ {
Capabilities = new DmpServerCapabilities Capabilities = new DmpServerCapabilities
{ {
ProtocolVersion = ServiceHost.ProviderProtocolVersion, ProtocolVersion = ServiceHost.ProviderProtocolVersion,
ProviderName = ServiceHost.ProviderName, ProviderName = ServiceHost.ProviderName,
ProviderDisplayName = ServiceHost.ProviderDescription, ProviderDisplayName = ServiceHost.ProviderDescription,
ConnectionProvider = ConnectionProviderOptionsHelper.BuildConnectionProviderOptions(), ConnectionProvider = ConnectionProviderOptionsHelper.BuildConnectionProviderOptions(),
AdminServicesProvider = AdminServicesProviderOptionsHelper.BuildAdminServicesProviderOptions() AdminServicesProvider = AdminServicesProviderOptionsHelper.BuildAdminServicesProviderOptions()
} }
} }
); );
} }
/// <summary> /// <summary>
/// Handles the version request. Sends back the server version as result. /// Handles the version request. Sends back the server version as result.
/// </summary> /// </summary>
private static async Task HandleVersionRequest( private static async Task HandleVersionRequest(
object versionRequestParams, object versionRequestParams,
RequestContext<string> requestContext) RequestContext<string> requestContext)
{ {
await requestContext.SendResult(serviceVersion.ToString()); await requestContext.SendResult(serviceVersion.ToString());
} }
#endregion #endregion
} }
} }

View File

@@ -4,6 +4,8 @@
// //
using Microsoft.SqlTools.ServiceLayer.Hosting;
namespace Microsoft.SqlTools.ServiceLayer.TaskServices.Contracts namespace Microsoft.SqlTools.ServiceLayer.TaskServices.Contracts
{ {
public class TaskInfo public class TaskInfo
@@ -34,6 +36,17 @@ namespace Microsoft.SqlTools.ServiceLayer.TaskServices.Contracts
/// </summary> /// </summary>
public string Name { get; set; } public string Name { get; set; }
/// <summary>
/// Provider Name
/// </summary>
public string ProviderName
{
get
{
return ServiceHost.ProviderName;
}
}
/// <summary> /// <summary>
/// Task description /// Task description
/// </summary> /// </summary>

View File

@@ -14,7 +14,7 @@ namespace Microsoft.SqlTools.ServiceLayer.TaskServices.Contracts
{ {
public static readonly public static readonly
EventType<TaskInfo> Type = EventType<TaskInfo> Type =
EventType<TaskInfo>.Create("task/newtaskcreated"); EventType<TaskInfo>.Create("tasks/newtaskcreated");
} }
/// <summary> /// <summary>
@@ -24,6 +24,6 @@ namespace Microsoft.SqlTools.ServiceLayer.TaskServices.Contracts
{ {
public static readonly public static readonly
EventType<TaskProgressInfo> Type = EventType<TaskProgressInfo> Type =
EventType<TaskProgressInfo>.Create("task/statuschanged"); EventType<TaskProgressInfo>.Create("tasks/statuschanged");
} }
} }

View File

@@ -20,7 +20,7 @@ namespace Microsoft.SqlTools.ServiceLayer.TaskServices
public class SqlTask : IDisposable public class SqlTask : IDisposable
{ {
private bool isCompleted; private bool isCompleted;
private bool isCanceled; private bool isCancelRequested;
private bool isDisposed; private bool isDisposed;
private readonly object lockObject = new object(); private readonly object lockObject = new object();
private readonly List<TaskMessage> messages = new List<TaskMessage>(); private readonly List<TaskMessage> messages = new List<TaskMessage>();
@@ -114,18 +114,18 @@ namespace Microsoft.SqlTools.ServiceLayer.TaskServices
/// Setting this to True will not change the Slot status. /// Setting this to True will not change the Slot status.
/// Setting the Slot status to Canceled will set this to true. /// Setting the Slot status to Canceled will set this to true.
/// </summary> /// </summary>
public bool IsCanceled public bool IsCancelRequested
{ {
get get
{ {
return isCanceled; return isCancelRequested;
} }
private set private set
{ {
if (isCanceled != value) if (isCancelRequested != value)
{ {
isCanceled = value; isCancelRequested = value;
OnTaskCanceled(); OnTaskCancelRequested();
} }
} }
} }
@@ -221,9 +221,9 @@ namespace Microsoft.SqlTools.ServiceLayer.TaskServices
throw new NotSupportedException("IsCompleted is not determined for status: " + status); throw new NotSupportedException("IsCompleted is not determined for status: " + status);
} }
if (status == SqlTaskStatus.Canceled) if (status == SqlTaskStatus.Canceled && !isCancelRequested)
{ {
IsCanceled = true; IsCancelRequested = true;
} }
OnStatusChanged(); OnStatusChanged();
@@ -251,7 +251,7 @@ namespace Microsoft.SqlTools.ServiceLayer.TaskServices
/// </summary> /// </summary>
public void Cancel() public void Cancel()
{ {
IsCanceled = true; IsCancelRequested = true;
} }
/// <summary> /// <summary>
@@ -308,11 +308,11 @@ namespace Microsoft.SqlTools.ServiceLayer.TaskServices
{ {
return new TaskInfo return new TaskInfo
{ {
TaskId = this.TaskId.ToString(),
DatabaseName = TaskMetadata.DatabaseName, DatabaseName = TaskMetadata.DatabaseName,
ServerName = TaskMetadata.ServerName, ServerName = TaskMetadata.ServerName,
Name = TaskMetadata.Name, Name = TaskMetadata.Name,
Description = TaskMetadata.Description, Description = TaskMetadata.Description,
TaskId = TaskId.ToString()
}; };
} }
@@ -366,7 +366,7 @@ namespace Microsoft.SqlTools.ServiceLayer.TaskServices
} }
} }
private void OnTaskCanceled() private void OnTaskCancelRequested()
{ {
var handler = TaskCanceled; var handler = TaskCanceled;
if (handler != null) if (handler != null)

View File

@@ -109,7 +109,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.TaskServices
sqlTask.Run().ContinueWith(task => { sqlTask.Run().ContinueWith(task => {
Assert.Equal(sqlTask.TaskStatus, expectedStatus); Assert.Equal(sqlTask.TaskStatus, expectedStatus);
Assert.Equal(sqlTask.IsCanceled, true); Assert.Equal(sqlTask.IsCancelRequested, true);
Assert.True(sqlTask.Duration > 0); Assert.True(sqlTask.Duration > 0);
}); });
Assert.Equal(sqlTask.TaskStatus, SqlTaskStatus.InProgress); Assert.Equal(sqlTask.TaskStatus, SqlTaskStatus.InProgress);
@@ -129,7 +129,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.TaskServices
sqlTask.Run().ContinueWith(task => { sqlTask.Run().ContinueWith(task => {
Assert.Equal(sqlTask.TaskStatus, expectedStatus); Assert.Equal(sqlTask.TaskStatus, expectedStatus);
Assert.Equal(sqlTask.IsCanceled, true); Assert.Equal(sqlTask.IsCancelRequested, true);
Assert.True(sqlTask.Duration > 0); Assert.True(sqlTask.Duration > 0);
}); });
Assert.Equal(sqlTask.TaskStatus, SqlTaskStatus.InProgress); Assert.Equal(sqlTask.TaskStatus, SqlTaskStatus.InProgress);

View File

@@ -74,7 +74,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.TaskServices
sqlTask.Run().ContinueWith(task => sqlTask.Run().ContinueWith(task =>
{ {
Assert.Equal(sqlTask.TaskStatus, expectedStatus); Assert.Equal(sqlTask.TaskStatus, expectedStatus);
Assert.Equal(sqlTask.IsCanceled, true); Assert.Equal(sqlTask.IsCancelRequested, true);
manager.Reset(); manager.Reset();
}); });

View File

@@ -57,7 +57,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.TaskServices
}); });
serviceHostMock.Verify(x => x.SendEvent(TaskCreatedNotification.Type, serviceHostMock.Verify(x => x.SendEvent(TaskCreatedNotification.Type,
It.Is<TaskInfo>(t => t.TaskId == sqlTask.TaskId.ToString())), Times.Once()); It.Is<TaskInfo>(t => t.TaskId == sqlTask.TaskId.ToString() && t.ProviderName == "MSSQL")), Times.Once());
operation.Stop(); operation.Stop();
serviceHostMock.Verify(x => x.SendEvent(TaskStatusChangedNotification.Type, serviceHostMock.Verify(x => x.SendEvent(TaskStatusChangedNotification.Type,