From a3eb280275a5f16425be004610fd1110eee9dbcf Mon Sep 17 00:00:00 2001 From: Chris Ross Date: Wed, 12 Mar 2014 15:07:37 -0700 Subject: [PATCH] Implement Program.Main and ServerManager. Add Startup search. --- samples/KWebStartup/Startup.cs | 21 +++++ samples/KWebStartup/project.json | 32 ++++++++ .../AssemblyNeutralAttribute.cs | 10 +++ src/Microsoft.AspNet.Hosting/HostingEngine.cs | 4 +- .../HostingServices.cs | 2 +- .../IApplicationEnvironment.cs | 14 ++++ src/Microsoft.AspNet.Hosting/Program.cs | 60 ++++++++++++++ .../Server/IServerFactory.cs | 2 + .../Server/IServerFactoryProvider.cs | 7 -- .../Server/IServerManager.cs | 2 +- .../Server/ServerManager.cs | 81 ++++++++++++++++++- .../ServerFactoryProvider.cs | 13 --- .../Startup/StartupLoader.cs | 81 ++++++++++++------- src/Microsoft.AspNet.Hosting/project.json | 3 + .../HostingEngineTests.cs | 12 +-- 15 files changed, 281 insertions(+), 63 deletions(-) create mode 100644 samples/KWebStartup/Startup.cs create mode 100644 samples/KWebStartup/project.json create mode 100644 src/Microsoft.AspNet.Hosting/AssemblyNeutralAttribute.cs create mode 100644 src/Microsoft.AspNet.Hosting/IApplicationEnvironment.cs create mode 100644 src/Microsoft.AspNet.Hosting/Program.cs delete mode 100644 src/Microsoft.AspNet.Hosting/Server/IServerFactoryProvider.cs delete mode 100644 src/Microsoft.AspNet.Hosting/ServerFactoryProvider.cs diff --git a/samples/KWebStartup/Startup.cs b/samples/KWebStartup/Startup.cs new file mode 100644 index 0000000000..cd080de5f2 --- /dev/null +++ b/samples/KWebStartup/Startup.cs @@ -0,0 +1,21 @@ +using Microsoft.AspNet.Abstractions; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace KWebStartup +{ + public class Startup + { + public void Configuration(IBuilder app) + { + app.Run(async context => + { + context.Response.ContentType = "text/plain"; + await context.Response.WriteAsync("Hello world"); + }); + } + } +} \ No newline at end of file diff --git a/samples/KWebStartup/project.json b/samples/KWebStartup/project.json new file mode 100644 index 0000000000..471e0621f6 --- /dev/null +++ b/samples/KWebStartup/project.json @@ -0,0 +1,32 @@ +{ + "version": "0.1-alpha-*", + "dependencies": { + "Microsoft.AspNet.Hosting": "0.1-alpha-*", + "Microsoft.AspNet.Server.WebListener": "0.1-alpha-*" + }, + "commands": { "web": "Microsoft.AspNet.Hosting server.name=Microsoft.AspNet.Server.WebListener" }, + "configurations": { + "net45": { + }, + "k10": { + "dependencies": { + "System.Console": "4.0.0.0", + "System.Collections": "4.0.0.0", + "System.Diagnostics.Debug": "4.0.10.0", + "System.Diagnostics.Tools": "4.0.0.0", + "System.Globalization": "4.0.10.0", + "System.IO": "4.0.0.0", + "System.IO.FileSystem": "4.0.0.0", + "System.IO.FileSystem.Primitives": "4.0.0.0", + "System.Linq": "4.0.0.0", + "System.Reflection": "4.0.10.0", + "System.Resources.ResourceManager": "4.0.0.0", + "System.Runtime": "4.0.20.0", + "System.Runtime.Extensions": "4.0.10.0", + "System.Runtime.InteropServices": "4.0.10.0", + "System.Text.Encoding": "4.0.10.0", + "System.Threading.Tasks": "4.0.0.0" + } + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Hosting/AssemblyNeutralAttribute.cs b/src/Microsoft.AspNet.Hosting/AssemblyNeutralAttribute.cs new file mode 100644 index 0000000000..2191d5198d --- /dev/null +++ b/src/Microsoft.AspNet.Hosting/AssemblyNeutralAttribute.cs @@ -0,0 +1,10 @@ +using System; + +namespace Microsoft.Net.Runtime +{ + [AssemblyNeutral] + [AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = true)] + public sealed class AssemblyNeutralAttribute : Attribute + { + } +} diff --git a/src/Microsoft.AspNet.Hosting/HostingEngine.cs b/src/Microsoft.AspNet.Hosting/HostingEngine.cs index 6a33dbde7d..da496d32e0 100644 --- a/src/Microsoft.AspNet.Hosting/HostingEngine.cs +++ b/src/Microsoft.AspNet.Hosting/HostingEngine.cs @@ -1,6 +1,7 @@ using System; using System.Threading; using Microsoft.AspNet.Abstractions; +using Microsoft.AspNet.DependencyInjection; using Microsoft.AspNet.Hosting.Builder; using Microsoft.AspNet.Hosting.Startup; using Microsoft.AspNet.Hosting.Server; @@ -54,12 +55,13 @@ namespace Microsoft.AspNet.Hosting private void EnsureServerFactory(HostingContext context) { + context.ServerFactory = context.ServerFactory ?? context.Services.GetService(); if (context.ServerFactory != null) { return; } - context.ServerFactory = _serverManager.GetServer(context.ServerName); + context.ServerFactory = _serverManager.GetServerFactory(context.ServerName); } private void EnsureApplicationDelegate(HostingContext context) diff --git a/src/Microsoft.AspNet.Hosting/HostingServices.cs b/src/Microsoft.AspNet.Hosting/HostingServices.cs index a8b90e96b4..d5f4d58a59 100644 --- a/src/Microsoft.AspNet.Hosting/HostingServices.cs +++ b/src/Microsoft.AspNet.Hosting/HostingServices.cs @@ -18,7 +18,7 @@ namespace Microsoft.AspNet.Hosting public static IEnumerable GetDefaultServices(IConfiguration configuration) { yield return DescribeService(configuration); - yield return DescribeService(configuration); + yield return DescribeService(configuration); yield return DescribeService(configuration); yield return DescribeService(configuration); diff --git a/src/Microsoft.AspNet.Hosting/IApplicationEnvironment.cs b/src/Microsoft.AspNet.Hosting/IApplicationEnvironment.cs new file mode 100644 index 0000000000..1c772bc835 --- /dev/null +++ b/src/Microsoft.AspNet.Hosting/IApplicationEnvironment.cs @@ -0,0 +1,14 @@ +using System; +using System.Runtime.Versioning; + +namespace Microsoft.Net.Runtime +{ + [AssemblyNeutral] + public interface IApplicationEnvironment + { + string ApplicationName { get; } + string Version { get; } + string ApplicationBasePath { get; } + FrameworkName TargetFramework { get; } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Hosting/Program.cs b/src/Microsoft.AspNet.Hosting/Program.cs new file mode 100644 index 0000000000..3b1f10ae33 --- /dev/null +++ b/src/Microsoft.AspNet.Hosting/Program.cs @@ -0,0 +1,60 @@ +using Microsoft.AspNet.ConfigurationModel; +using Microsoft.AspNet.ConfigurationModel.Sources; +using Microsoft.AspNet.DependencyInjection; +using Microsoft.AspNet.Hosting.Server; +using Microsoft.Net.Runtime; +using System; +using System.IO; + +namespace Microsoft.AspNet.Hosting +{ + public class Program + { + private const string HostingIniFile = "Microsoft.AspNet.Hosting.ini"; + + private readonly IServiceProvider _serviceProvider; + + public Program(IServiceProvider serviceProvider) + { + _serviceProvider = serviceProvider; + } + + public void Main(string[] args) + { + var config = new Configuration(); + config.AddCommandLine(args); + config.AddEnvironmentVariables(); + if (File.Exists(HostingIniFile)) + { + config.AddIniFile(HostingIniFile); + } + + var services = new ServiceProvider(_serviceProvider) + .Add(HostingServices.GetDefaultServices(config)); + + var appEnvironment = _serviceProvider.GetService(); + + var context = new HostingContext() + { + Services = services, + ServerName = config.Get("server.name"), // TODO: Key names + ApplicationName = config.Get("app.name") // TODO: Key names + ?? appEnvironment.ApplicationName, + }; + + var engine = services.GetService(); + if (engine == null) + { + throw new Exception("TODO: IHostingEngine service not available exception"); + } + + using (engine.Start(context)) + { +#if NET45 + Console.WriteLine("Started"); + Console.ReadLine(); +#endif + } + } + } +} diff --git a/src/Microsoft.AspNet.Hosting/Server/IServerFactory.cs b/src/Microsoft.AspNet.Hosting/Server/IServerFactory.cs index 2d8350d53e..368004e197 100644 --- a/src/Microsoft.AspNet.Hosting/Server/IServerFactory.cs +++ b/src/Microsoft.AspNet.Hosting/Server/IServerFactory.cs @@ -1,9 +1,11 @@ using System; using System.Threading.Tasks; using Microsoft.AspNet.Abstractions; +using Microsoft.Net.Runtime; namespace Microsoft.AspNet.Hosting.Server { + [AssemblyNeutral] public interface IServerFactory { IDisposable Start(Func application); diff --git a/src/Microsoft.AspNet.Hosting/Server/IServerFactoryProvider.cs b/src/Microsoft.AspNet.Hosting/Server/IServerFactoryProvider.cs deleted file mode 100644 index 714a51ba8b..0000000000 --- a/src/Microsoft.AspNet.Hosting/Server/IServerFactoryProvider.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Microsoft.AspNet.Hosting.Server -{ - public interface IServerFactoryProvider - { - IServerFactory GetServerFactory(string serverName); - } -} diff --git a/src/Microsoft.AspNet.Hosting/Server/IServerManager.cs b/src/Microsoft.AspNet.Hosting/Server/IServerManager.cs index 471f9445bd..5a6b6b9166 100644 --- a/src/Microsoft.AspNet.Hosting/Server/IServerManager.cs +++ b/src/Microsoft.AspNet.Hosting/Server/IServerManager.cs @@ -2,6 +2,6 @@ namespace Microsoft.AspNet.Hosting.Server { public interface IServerManager { - IServerFactory GetServer(string serverName); + IServerFactory GetServerFactory(string serverName); } } \ No newline at end of file diff --git a/src/Microsoft.AspNet.Hosting/Server/ServerManager.cs b/src/Microsoft.AspNet.Hosting/Server/ServerManager.cs index a6b91be91d..a35e3a9fb6 100644 --- a/src/Microsoft.AspNet.Hosting/Server/ServerManager.cs +++ b/src/Microsoft.AspNet.Hosting/Server/ServerManager.cs @@ -1,12 +1,89 @@ +using Microsoft.AspNet.DependencyInjection; using System; +using System.Linq; +using System.Reflection; namespace Microsoft.AspNet.Hosting.Server { public class ServerManager : IServerManager { - public IServerFactory GetServer(string serverName) + private readonly IServiceProvider _services; + + public ServerManager(IServiceProvider services) { - throw new NotImplementedException(); + _services = services; + } + + public IServerFactory GetServerFactory(string serverFactoryIdentifier) + { + if (string.IsNullOrWhiteSpace(serverFactoryIdentifier)) + { + throw new ArgumentNullException("serverFactoryIdentifier"); + } + + string typeName; + string assemblyName; + var parts = serverFactoryIdentifier.Split(new[] { ',' }, 2); + if (parts.Length == 1) + { + typeName = null; + assemblyName = serverFactoryIdentifier; + } + else if (parts.Length == 2) + { + typeName = parts[0]; + assemblyName = parts[1]; + } + else + { + throw new ArgumentException("TODO: Unrecognized format", "serverFactoryIdentifier"); + } + + var assembly = Assembly.Load(new AssemblyName(assemblyName)); + if (assembly == null) + { + throw new Exception(String.Format("TODO: assembly {0} failed to load message", assemblyName)); + } + + Type type = null; + Type interfaceInfo; + if (string.IsNullOrWhiteSpace(typeName)) + { + foreach (var typeInfo in assembly.DefinedTypes) + { + interfaceInfo = typeInfo.ImplementedInterfaces.FirstOrDefault(interf => + interf.FullName == typeof(IServerFactory).FullName); + if (interfaceInfo != null) + { + type = typeInfo.AsType(); + } + } + + if (type == null) + { + throw new Exception(String.Format("TODO: type {0} failed to load message", typeName ?? "")); + } + } + else + { + type = assembly.GetType(typeName) ?? assembly.GetType(assemblyName + "." + typeName); + + if (type == null) + { + throw new Exception(String.Format("TODO: type {0} failed to load message", typeName ?? "")); + } + + interfaceInfo = type.GetTypeInfo().ImplementedInterfaces.FirstOrDefault(interf => + interf.FullName == typeof(IServerFactory).FullName); + + if (interfaceInfo == null) + { + throw new Exception("TODO: IServerFactory interface not found"); + } + } + + object instance = ActivatorUtilities.GetServiceOrCreateInstance(_services, type); + return (IServerFactory)instance; } } } \ No newline at end of file diff --git a/src/Microsoft.AspNet.Hosting/ServerFactoryProvider.cs b/src/Microsoft.AspNet.Hosting/ServerFactoryProvider.cs deleted file mode 100644 index eb5743a86a..0000000000 --- a/src/Microsoft.AspNet.Hosting/ServerFactoryProvider.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using Microsoft.AspNet.Hosting.Server; - -namespace Microsoft.AspNet.Hosting -{ - public class ServerFactoryProvider : IServerFactoryProvider - { - public IServerFactory GetServerFactory(string serverFactoryIdentifier) - { - throw new NotImplementedException(); - } - } -} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Hosting/Startup/StartupLoader.cs b/src/Microsoft.AspNet.Hosting/Startup/StartupLoader.cs index 2ed642f959..abdd853bbf 100644 --- a/src/Microsoft.AspNet.Hosting/Startup/StartupLoader.cs +++ b/src/Microsoft.AspNet.Hosting/Startup/StartupLoader.cs @@ -25,38 +25,61 @@ namespace Microsoft.AspNet.Hosting.Startup return _next.LoadStartup(applicationName, diagnosticMessages); } + string typeName; + string assemblyName; var parts = applicationName.Split(new[] { ',' }, 2); - if (parts.Length == 2) + if (parts.Length == 1) { - var typeName = parts[0]; - var assemblyName = parts[1]; - - var assembly = Assembly.Load(new AssemblyName(assemblyName)); - if (assembly == null) - { - throw new Exception(String.Format("TODO: assembly {0} failed to load message", assemblyName)); - } - - var type = assembly.GetType(typeName) ?? assembly.GetType(assemblyName + "." + typeName); - if (type == null) - { - throw new Exception(String.Format("TODO: type {0} failed to load message", typeName)); - } - - var methodInfo = type.GetTypeInfo().GetDeclaredMethod("Configuration"); - if (methodInfo == null) - { - throw new Exception("TODO: Configuration method not found"); - } - - object instance = null; - if (!methodInfo.IsStatic) - { - instance = ActivatorUtilities.GetServiceOrCreateInstance(_services, type); - } - return builder => methodInfo.Invoke(instance, new object[] { builder }); + typeName = null; + assemblyName = applicationName; } - throw new Exception("TODO: Unrecognized format"); + else if (parts.Length == 2) + { + typeName = parts[0]; + assemblyName = parts[1]; + } + else + { + throw new Exception("TODO: Unrecognized format"); + } + + var assembly = Assembly.Load(new AssemblyName(assemblyName)); + if (assembly == null) + { + throw new Exception(String.Format("TODO: assembly {0} failed to load message", assemblyName)); + } + + Type type = null; + if (string.IsNullOrWhiteSpace(typeName)) + { + var typeInfo = assembly.DefinedTypes.FirstOrDefault(aType => aType.Name.Equals("Startup")); + if (typeInfo != null) + { + type = typeInfo.AsType(); + } + } + else + { + type = assembly.GetType(typeName); + } + + if (type == null) + { + throw new Exception(String.Format("TODO: type {0} failed to load message", typeName)); + } + + var methodInfo = type.GetTypeInfo().GetDeclaredMethod("Configuration"); + if (methodInfo == null) + { + throw new Exception("TODO: Configuration method not found"); + } + + object instance = null; + if (!methodInfo.IsStatic) + { + instance = ActivatorUtilities.GetServiceOrCreateInstance(_services, type); + } + return builder => methodInfo.Invoke(instance, new object[] { builder }); } } } diff --git a/src/Microsoft.AspNet.Hosting/project.json b/src/Microsoft.AspNet.Hosting/project.json index 0577dc0777..07acc75da3 100644 --- a/src/Microsoft.AspNet.Hosting/project.json +++ b/src/Microsoft.AspNet.Hosting/project.json @@ -14,6 +14,9 @@ "System.Collections": "4.0.0.0", "System.ComponentModel": "4.0.0.0", "System.Diagnostics.Debug": "4.0.10.0", + "System.IO": "4.0.0.0", + "System.IO.FileSystem": "4.0.0.0", + "System.IO.FileSystem.Primitives": "4.0.0.0", "System.Linq": "4.0.0.0", "System.Reflection": "4.0.10.0", "System.Runtime": "4.0.20.0", diff --git a/test/Microsoft.AspNet.Hosting.Tests/HostingEngineTests.cs b/test/Microsoft.AspNet.Hosting.Tests/HostingEngineTests.cs index 74580b3a5e..476e1a4454 100644 --- a/test/Microsoft.AspNet.Hosting.Tests/HostingEngineTests.cs +++ b/test/Microsoft.AspNet.Hosting.Tests/HostingEngineTests.cs @@ -10,7 +10,7 @@ using Xunit; namespace Microsoft.AspNet.Hosting.Tests { - public class HostingEngineTests : IServerManager, IServerFactory + public class HostingEngineTests : IServerFactory { private readonly IList _startInstances = new List(); @@ -29,14 +29,13 @@ namespace Microsoft.AspNet.Hosting.Tests public void HostingEngineCanBeStarted() { var services = new ServiceProvider() - .Add(HostingServices.GetDefaultServices() - .Where(descriptor => descriptor.ServiceType != typeof(IServerManager))) - .AddInstance(this); + .Add(HostingServices.GetDefaultServices()); var engine = services.GetService(); var context = new HostingContext { + ServerFactory = this, ApplicationName = "Microsoft.AspNet.Hosting.Tests.Fakes.FakeStartup, Microsoft.AspNet.Hosting.Tests" }; @@ -51,11 +50,6 @@ namespace Microsoft.AspNet.Hosting.Tests Assert.Equal(1, _startInstances[0].DisposeCalls); } - public IServerFactory GetServer(string serverName) - { - return this; - } - public void Initialize(IBuilder builder) {