Feature/serialization exe (#442)

* Initial changes to have serialization generate its own .exe

* Removed additional project from sln file

* remove all references to removed temporary project

* Moved shared contracts into own dll and fixed imports. Addressed PR comments

* Undid having a separate contracts project since that'll be a task for later on. Moved dbcellvalue and saveresultsrequest to Hosting, where they will be imported and shared by the service layer and serialization projects

* Switched backslashes in project reference in csproj file to forward slashes for consistency

* Moved necessary contracts back to service layer. Refactored CommandOptions to reduce code duplication. Addressed miscellaneous PR suggestions

* Accidentally left these files out of previous commit

* Initialized loggers for serialization and credentials with the logging directory provided by the cmd line arg, if there is one

* Changed default log directory paths for serialization and credentials. Removed unnecessary cast and added a copyright

* Changed name of generated executable for serialization service

* removed unnecessary object cast

* removing unnecessary imports and addressing other PR comments
This commit is contained in:
Henry Phan
2017-09-05 16:21:42 -07:00
committed by GitHub
parent 22ccac98ae
commit 784f4c5d05
26 changed files with 425 additions and 177 deletions

View File

@@ -1,175 +0,0 @@
//
// 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.Reflection;
using System.Threading.Tasks;
using Microsoft.SqlTools.Hosting;
using Microsoft.SqlTools.Hosting.Contracts;
using Microsoft.SqlTools.Hosting.Protocol;
using Microsoft.SqlTools.Hosting.Protocol.Channel;
using Microsoft.SqlTools.Utility;
namespace Microsoft.SqlTools.Credentials
{
/// <summary>
/// SQL Tools Credentials Service request handler. Provides the entire JSON RPC
/// implementation for sending/receiving JSON requests and dispatching the requests to
/// handlers that are registered prior to startup.
/// </summary>
public sealed class CredentialsServiceHost : ServiceHostBase
{
/// <summary>
/// This timeout limits the amount of time that shutdown tasks can take to complete
/// prior to the process shutting down.
/// </summary>
private const int ShutdownTimeoutInSeconds = 120;
#region Singleton Instance Code
/// <summary>
/// Singleton instance of the service host for internal storage
/// </summary>
private static readonly Lazy<CredentialsServiceHost> instance = new Lazy<CredentialsServiceHost>(() => new CredentialsServiceHost());
/// <summary>
/// Current instance of the ServiceHost
/// </summary>
public static CredentialsServiceHost Instance
{
get { return instance.Value; }
}
/// <summary>
/// Constructs new instance of ServiceHost using the host and profile details provided.
/// Access is private to ensure only one instance exists at a time.
/// </summary>
private CredentialsServiceHost() : base(new StdioServerChannel())
{
// Initialize the shutdown activities
shutdownCallbacks = new List<ShutdownCallback>();
initializeCallbacks = new List<InitializeCallback>();
}
/// <summary>
/// Provide initialization that must occur after the service host is started
/// </summary>
public void InitializeRequestHandlers()
{
// Register the requests that this service host will handle
this.SetRequestHandler(InitializeRequest.Type, this.HandleInitializeRequest);
this.SetRequestHandler(ShutdownRequest.Type, this.HandleShutdownRequest);
this.SetRequestHandler(VersionRequest.Type, HandleVersionRequest);
}
#endregion
#region Member Variables
/// <summary>
/// Delegate definition for the host shutdown event
/// </summary>
/// <param name="shutdownParams"></param>
/// <param name="shutdownRequestContext"></param>
public delegate Task ShutdownCallback(object shutdownParams, RequestContext<object> shutdownRequestContext);
/// <summary>
/// Delegate definition for the host initialization event
/// </summary>
/// <param name="startupParams"></param>
/// <param name="requestContext"></param>
public delegate Task InitializeCallback(InitializeRequest startupParams, RequestContext<InitializeResult> requestContext);
private readonly List<ShutdownCallback> shutdownCallbacks;
private readonly List<InitializeCallback> initializeCallbacks;
private static readonly Version serviceVersion = Assembly.GetEntryAssembly().GetName().Version;
#endregion
#region Public Methods
/// <summary>
/// Adds a new callback to be called when the shutdown request is submitted
/// </summary>
/// <param name="callback">Callback to perform when a shutdown request is submitted</param>
public void RegisterShutdownTask(ShutdownCallback callback)
{
shutdownCallbacks.Add(callback);
}
/// <summary>
/// Add a new method to be called when the initialize request is submitted
/// </summary>
/// <param name="callback">Callback to perform when an initialize request is submitted</param>
public void RegisterInitializeTask(InitializeCallback callback)
{
initializeCallbacks.Add(callback);
}
#endregion
#region Request Handlers
/// <summary>
/// Handles the shutdown event for the Language Server
/// </summary>
private async Task HandleShutdownRequest(object shutdownParams, RequestContext<object> requestContext)
{
Logger.Write(LogLevel.Normal, "Service host is shutting down...");
// Call all the shutdown methods provided by the service components
Task[] shutdownTasks = shutdownCallbacks.Select(t => t(shutdownParams, requestContext)).ToArray();
TimeSpan shutdownTimeout = TimeSpan.FromSeconds(ShutdownTimeoutInSeconds);
// 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));
}
/// <summary>
/// Handles the initialization request
/// </summary>
/// <param name="initializeParams"></param>
/// <param name="requestContext"></param>
/// <returns></returns>
internal async Task HandleInitializeRequest(InitializeRequest initializeParams, RequestContext<InitializeResult> requestContext)
{
// Call all tasks that registered on the initialize request
var initializeTasks = initializeCallbacks.Select(t => t(initializeParams, requestContext));
await Task.WhenAll(initializeTasks);
// TODO: Figure out where this needs to go to be agnostic of the language
// Send back what this server can do
await requestContext.SendResult(
new InitializeResult
{
Capabilities = new ServerCapabilities
{
DefinitionProvider = false,
ReferencesProvider = false,
DocumentFormattingProvider = false,
DocumentRangeFormattingProvider = false,
DocumentHighlightProvider = false,
HoverProvider = false
}
});
}
/// <summary>
/// Handles the version request. Sends back the server version as result.
/// </summary>
private static async Task HandleVersionRequest(
object versionRequestParams,
RequestContext<string> requestContext)
{
await requestContext.SendResult(serviceVersion.ToString());
}
#endregion
}
}

View File

@@ -6,8 +6,9 @@ using Microsoft.SqlTools.Extensibility;
using Microsoft.SqlTools.Hosting;
using Microsoft.SqlTools.Hosting.Protocol;
using Microsoft.SqlTools.ServiceLayer.SqlContext;
using Microsoft.SqlTools.Utility;
namespace Microsoft.SqlTools.Credentials
namespace Microsoft.SqlTools.Credentials.Utility
{
/// <summary>
/// Provides support for starting up a service host. This is a common responsibility
@@ -18,9 +19,9 @@ namespace Microsoft.SqlTools.Credentials
private static object lockObject = new object();
private static bool isLoaded;
internal static CredentialsServiceHost CreateAndStartServiceHost(SqlToolsContext sqlToolsContext)
internal static UtilityServiceHost CreateAndStartServiceHost(SqlToolsContext sqlToolsContext)
{
CredentialsServiceHost serviceHost = CredentialsServiceHost.Instance;
UtilityServiceHost serviceHost = UtilityServiceHost.Instance;
lock (lockObject)
{
if (!isLoaded)
@@ -40,7 +41,7 @@ namespace Microsoft.SqlTools.Credentials
return serviceHost;
}
private static void InitializeRequestHandlersAndServices(CredentialsServiceHost serviceHost, SqlToolsContext sqlToolsContext)
private static void InitializeRequestHandlersAndServices(UtilityServiceHost serviceHost, SqlToolsContext sqlToolsContext)
{
// Load extension provider, which currently finds all exports in current DLL. Can be changed to find based
// on directory or assembly list quite easily in the future

View File

@@ -35,4 +35,7 @@
<ItemGroup>
<EmbeddedResource Include="Localization\sr.resx" />
</ItemGroup>
<ItemGroup>
<Folder Include="Utility\" />
</ItemGroup>
</Project>

View File

@@ -5,6 +5,7 @@
using System;
using System.IO;
using Microsoft.SqlTools.Credentials.Utility;
using Microsoft.SqlTools.Hosting.Utility;
using Microsoft.SqlTools.ServiceLayer.SqlContext;
using Microsoft.SqlTools.Utility;
@@ -15,6 +16,8 @@ namespace Microsoft.SqlTools.Credentials
/// </summary>
internal class Program
{
private const string ServiceName = "MicrosoftSqlToolsCredentials.exe";
/// <summary>
/// Main entry point into the Credentials Service Host
/// </summary>
@@ -23,7 +26,7 @@ namespace Microsoft.SqlTools.Credentials
try
{
// read command-line arguments
CommandOptions commandOptions = new CommandOptions(args);
CommandOptions commandOptions = new CommandOptions(args, ServiceName);
if (commandOptions.ShouldExit)
{
return;
@@ -37,7 +40,7 @@ namespace Microsoft.SqlTools.Credentials
// turn on Verbose logging during early development
// we need to switch to Normal when preparing for public preview
Logger.Initialize(logFilePath, minimumLogLevel: LogLevel.Verbose, isEnabled: commandOptions.EnableLogging);
Logger.Initialize(logFilePath: logFilePath, minimumLogLevel: LogLevel.Verbose, isEnabled: commandOptions.EnableLogging);
Logger.Write(LogLevel.Normal, "Starting SqlTools Credentials Provider");
// set up the host details and profile paths
@@ -47,7 +50,7 @@ namespace Microsoft.SqlTools.Credentials
version: new Version(1, 0));
SqlToolsContext sqlToolsContext = new SqlToolsContext(hostDetails);
CredentialsServiceHost serviceHost = HostLoader.CreateAndStartServiceHost(sqlToolsContext);
UtilityServiceHost serviceHost = HostLoader.CreateAndStartServiceHost(sqlToolsContext);
serviceHost.WaitForExit();
}

View File

@@ -1,113 +0,0 @@
//
// 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.IO;
namespace Microsoft.SqlTools.Credentials.Utility
{
/// <summary>
/// The command-line options helper class.
/// </summary>
internal class CommandOptions
{
/// <summary>
/// Construct and parse command line options from the arguments array
/// </summary>
public CommandOptions(string[] args)
{
ErrorMessage = string.Empty;
try
{
for (int i = 0; i < args.Length; ++i)
{
string arg = args[i];
if (arg.StartsWith("--") || arg.StartsWith("-"))
{
if (arg.StartsWith("--"))
{
// Extracting arguments and properties
arg = arg.Substring(1).ToLowerInvariant();
}
switch (arg)
{
case "-enable-logging":
EnableLogging = true;
break;
case "-log-dir":
SetLoggingDirectory(args[++i]);
break;
case "h":
case "-help":
ShouldExit = true;
return;
default:
ErrorMessage += String.Format("Unknown argument \"{0}\"" + Environment.NewLine, arg);
break;
}
}
}
}
catch (Exception ex)
{
ErrorMessage += ex.ToString();
return;
}
finally
{
if (!string.IsNullOrEmpty(ErrorMessage) || ShouldExit)
{
Console.WriteLine(Usage);
ShouldExit = true;
}
}
}
internal string ErrorMessage { get; private set; }
/// <summary>
/// Whether diagnostic logging is enabled
/// </summary>
public bool EnableLogging { get; private set; }
/// <summary>
/// Gets the directory where log files are output.
/// </summary>
public string LoggingDirectory { get; private set; }
/// <summary>
/// Whether the program should exit immediately. Set to true when the usage is printed.
/// </summary>
public bool ShouldExit { get; private set; }
private void SetLoggingDirectory(string loggingDirectory)
{
if (string.IsNullOrWhiteSpace(loggingDirectory))
{
return;
}
this.LoggingDirectory = Path.GetFullPath(loggingDirectory);
}
/// <summary>
/// Get the usage string describing command-line arguments for the program
/// </summary>
public string Usage
{
get
{
var str = string.Format("{0}" + Environment.NewLine +
"Microsoft.SqlTools.Credentials " + Environment.NewLine +
" Options:" + Environment.NewLine +
" [--enable-logging]" + Environment.NewLine +
" [--help]" + Environment.NewLine,
ErrorMessage);
return str;
}
}
}
}