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

@@ -0,0 +1,83 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
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.Serialization
{
/// <summary>
/// Provides support for starting up a service host. This is a common responsibility
/// for both the main service program and test driver that interacts with it
/// </summary>
public static class HostLoader
{
private static object lockObject = new object();
private static bool isLoaded;
internal static UtilityServiceHost CreateAndStartServiceHost(SqlToolsContext sqlToolsContext)
{
UtilityServiceHost serviceHost = UtilityServiceHost.Instance;
lock (lockObject)
{
if (!isLoaded)
{
// Grab the instance of the service host
serviceHost.Initialize();
InitializeRequestHandlersAndServices(serviceHost, sqlToolsContext);
// Start the service only after all request handlers are setup. This is vital
// as otherwise the Initialize event can be lost - it's processed and discarded before the handler
// is hooked up to receive the message
serviceHost.Start().Wait();
isLoaded = true;
}
}
return serviceHost;
}
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
ExtensionServiceProvider serviceProvider = ExtensionServiceProvider.CreateDefaultServiceProvider();
serviceProvider.RegisterSingleService(sqlToolsContext);
serviceProvider.RegisterSingleService(serviceHost);
SerializationService serializationService = serviceProvider.GetService<SerializationService>();
serializationService.InitializeService(serviceHost);
serviceProvider.RegisterSingleService(serializationService);
InitializeHostedServices(serviceProvider, serviceHost);
serviceHost.InitializeRequestHandlers();
}
/// <summary>
/// Internal to support testing. Initializes <see cref="IHostedService"/> instances in the service,
/// and registers them for their preferred service type
/// </summary>
internal static void InitializeHostedServices(RegisteredServiceProvider provider, IProtocolEndpoint host)
{
// Pre-register all services before initializing. This ensures that if one service wishes to reference
// another one during initialization, it will be able to safely do so
foreach (IHostedService service in provider.GetServices<IHostedService>())
{
provider.RegisterSingleService(service.ServiceType, service);
}
foreach (IHostedService service in provider.GetServices<IHostedService>())
{
// Initialize all hosted services, and register them in the service provider for their requested
// service type. This ensures that when searching for the ConnectionService you can get it without
// searching for an IHostedService of type ConnectionService
service.InitializeService(host);
}
}
}
}

View File

@@ -0,0 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.0</TargetFramework>
<PackageId>SqlSerializationService</PackageId>
<AssemblyName>SqlSerializationService</AssemblyName>
<Company>Microsoft</Company>
<Product>Sql Tools Serialization Service</Product>
<Description>Provides serialization functionality.</Description>
<Copyright><EFBFBD> Microsoft Corporation. All rights reserved.</Copyright>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DefineConstants>TRACE;DEBUG;NETCOREAPP1_0;NETCOREAPP2_0</DefineConstants>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Microsoft.SqlTools.Hosting\Microsoft.SqlTools.Hosting.csproj" />
<ProjectReference Include="..\Microsoft.SqlTools.ServiceLayer\Microsoft.SqlTools.ServiceLayer.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,63 @@
//
// 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;
using Microsoft.SqlTools.Hosting.Utility;
using Microsoft.SqlTools.ServiceLayer.SqlContext;
using Microsoft.SqlTools.Utility;
namespace Microsoft.SqlTools.Serialization
{
/// <summary>
/// Main application class for Serialization Service Host executable
/// </summary>
internal class Program
{
private const string ServiceName = "SqlSerializationService.exe";
/// <summary>
/// Main entry point into the Serialization Service Host
/// </summary>
internal static void Main(string[] args)
{
try
{
// read command-line arguments
CommandOptions commandOptions = new CommandOptions(args, ServiceName);
if (commandOptions.ShouldExit)
{
return;
}
string logFilePath = "serialization";
if (!string.IsNullOrWhiteSpace(commandOptions.LoggingDirectory))
{
logFilePath = Path.Combine(commandOptions.LoggingDirectory, logFilePath);
}
// turn on Verbose logging during early development
// we need to switch to Normal when preparing for public preview
Logger.Initialize(logFilePath: logFilePath, minimumLogLevel: LogLevel.Verbose, isEnabled: commandOptions.EnableLogging);
Logger.Write(LogLevel.Normal, "Starting SqlTools Serialization Provider");
// set up the host details and profile paths
var hostDetails = new HostDetails(
name: "SqlTools Serialization Provider",
profileId: "Microsoft.SqlTools.Serialization",
version: new Version(1, 0));
SqlToolsContext sqlToolsContext = new SqlToolsContext(hostDetails);
UtilityServiceHost serviceHost = HostLoader.CreateAndStartServiceHost(sqlToolsContext);
serviceHost.WaitForExit();
}
catch (Exception e)
{
Logger.Write(LogLevel.Error, string.Format("An unhandled exception occurred: {0}", e));
Environment.Exit(1);
}
}
}
}

View File

@@ -0,0 +1,78 @@
//
// 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;
using System.Threading.Tasks;
using Microsoft.SqlTools.Extensibility;
using Microsoft.SqlTools.Hosting;
using Microsoft.SqlTools.Hosting.Protocol;
using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
using Microsoft.SqlTools.Utility;
namespace Microsoft.SqlTools.Serialization
{
/// <summary>
/// Service responsible for securing credentials in a platform-neutral manner. This provides
/// a generic API for read, save and delete credentials
/// </summary>
[Export(typeof(IHostedService))]
public class SerializationService : HostedService<SerializationService>, IComposableService
{
public override void InitializeService(IProtocolEndpoint serviceHost)
{
Logger.Write(LogLevel.Verbose, "Serialization initialized");
// Register request and event handlers with the Service Host
serviceHost.SetRequestHandler(SaveAsRequest.Type, HandleSaveAsRequest);
}
public async Task HandleSaveAsRequest(SaveResultsInfo resultsInfo, RequestContext<SaveResultRequestResult> requestContext)
{
Func<Task<SaveResultRequestResult>> doSave = () =>
{
return SaveAsAsync(resultsInfo, requestContext);
};
await HandleRequest(doSave, requestContext, "HandleSaveAsRequest");
}
public async Task<SaveResultRequestResult> SaveAsAsync(SaveResultsInfo resultsInfo, RequestContext<SaveResultRequestResult> requestContext)
{
// TODO: Refactor currently available serialization code in sqltools to be utilized here
// Issue here: https://github.com/Microsoft/carbon/issues/1789
switch (resultsInfo.SaveFormat) {
case "json":
throw new NotImplementedException("Converting to " + resultsInfo.SaveFormat + " is not implemented.");
break;
case "csv":
throw new NotImplementedException("Converting to " + resultsInfo.SaveFormat + " is not implemented.");
break;
case "excel":
throw new NotImplementedException("Converting to " + resultsInfo.SaveFormat + " is not implemented.");
break;
default:
throw new NotImplementedException("Converting to " + resultsInfo.SaveFormat + " is not implemented.");
}
}
private async Task HandleRequest<T>(Func<Task<T>> handler, RequestContext<T> requestContext, string requestType)
{
Logger.Write(LogLevel.Verbose, requestType);
try
{
T result = await handler();
await requestContext.SendResult(result);
}
catch (Exception ex)
{
await requestContext.SendError(ex.ToString());
}
}
}
}