diff --git a/Samples/AzureFullDuplex/AzureFullDuplex.suo b/Samples/AzureFullDuplex/AzureFullDuplex.suo index 025c7d5e919..12bf1c1a308 100644 Binary files a/Samples/AzureFullDuplex/AzureFullDuplex.suo and b/Samples/AzureFullDuplex/AzureFullDuplex.suo differ diff --git a/Samples/AzureFullDuplex/OrderService/EndpointConfiguration.cs b/Samples/AzureFullDuplex/OrderService/EndpointConfiguration.cs index 9613a05ba71..ca74146eeee 100644 --- a/Samples/AzureFullDuplex/OrderService/EndpointConfiguration.cs +++ b/Samples/AzureFullDuplex/OrderService/EndpointConfiguration.cs @@ -2,5 +2,5 @@ namespace OrderService { - public class EndpointConfiguration : IConfigureThisEndpoint, AsA_Server { } + public class EndpointConfiguration : IConfigureThisEndpoint, AsA_Worker { } } \ No newline at end of file diff --git a/Samples/AzureFullDuplex/ServiceConfiguration.cscfg b/Samples/AzureFullDuplex/ServiceConfiguration.cscfg index c6e63b497b3..6bdc1908231 100644 --- a/Samples/AzureFullDuplex/ServiceConfiguration.cscfg +++ b/Samples/AzureFullDuplex/ServiceConfiguration.cscfg @@ -18,6 +18,7 @@ + diff --git a/Samples/AzureFullDuplex/ServiceDefinition.csdef b/Samples/AzureFullDuplex/ServiceDefinition.csdef index f057586d035..f8fee760421 100644 --- a/Samples/AzureFullDuplex/ServiceDefinition.csdef +++ b/Samples/AzureFullDuplex/ServiceDefinition.csdef @@ -25,6 +25,7 @@ + diff --git a/Samples/AzurePubSub/OrderService/EndpointConfiguration.cs b/Samples/AzurePubSub/OrderService/EndpointConfiguration.cs index d777a78e67c..ca74146eeee 100644 --- a/Samples/AzurePubSub/OrderService/EndpointConfiguration.cs +++ b/Samples/AzurePubSub/OrderService/EndpointConfiguration.cs @@ -2,5 +2,5 @@ namespace OrderService { - public class EndpointConfiguration : IConfigureThisEndpoint, AsA_Publisher { } + public class EndpointConfiguration : IConfigureThisEndpoint, AsA_Worker { } } \ No newline at end of file diff --git a/Samples/AzureThumbnailCreator/Service/EndpointConfiguration.cs b/Samples/AzureThumbnailCreator/Service/EndpointConfiguration.cs index 5bf83d22089..a01d5db1234 100644 --- a/Samples/AzureThumbnailCreator/Service/EndpointConfiguration.cs +++ b/Samples/AzureThumbnailCreator/Service/EndpointConfiguration.cs @@ -2,7 +2,7 @@ namespace OrderService { - public class EndpointConfiguration : IConfigureThisEndpoint, AsA_Publisher + public class EndpointConfiguration : IConfigureThisEndpoint, AsA_Worker { } diff --git a/Samples/AzureThumbnailCreator/ServiceConfiguration.cscfg b/Samples/AzureThumbnailCreator/ServiceConfiguration.cscfg index ded3d2c259a..14fa3c53956 100644 --- a/Samples/AzureThumbnailCreator/ServiceConfiguration.cscfg +++ b/Samples/AzureThumbnailCreator/ServiceConfiguration.cscfg @@ -19,6 +19,7 @@ + diff --git a/Samples/AzureThumbnailCreator/ServiceDefinition.build.csdef b/Samples/AzureThumbnailCreator/ServiceDefinition.build.csdef index b44e57a425c..476d0c71abc 100644 --- a/Samples/AzureThumbnailCreator/ServiceDefinition.build.csdef +++ b/Samples/AzureThumbnailCreator/ServiceDefinition.build.csdef @@ -27,7 +27,6 @@ - @@ -36,6 +35,7 @@ + @@ -45,7 +45,6 @@ - \ No newline at end of file diff --git a/Samples/AzureThumbnailCreator/ServiceDefinition.csdef b/Samples/AzureThumbnailCreator/ServiceDefinition.csdef index 7ccae5d75a4..97d7874d43f 100644 --- a/Samples/AzureThumbnailCreator/ServiceDefinition.csdef +++ b/Samples/AzureThumbnailCreator/ServiceDefinition.csdef @@ -26,6 +26,7 @@ + diff --git a/Samples/GenericHost/GenericHost.suo b/Samples/GenericHost/GenericHost.suo index a455ef20d12..af022d1ad17 100644 Binary files a/Samples/GenericHost/GenericHost.suo and b/Samples/GenericHost/GenericHost.suo differ diff --git a/lib/Ionic.Zip.dll b/lib/Ionic.Zip.dll new file mode 100644 index 00000000000..95fa9288557 Binary files /dev/null and b/lib/Ionic.Zip.dll differ diff --git a/src/azure/Integration/NServiceBus.Integration.Azure/AzureAppender.cs b/src/azure/Integration/NServiceBus.Integration.Azure/AzureAppender.cs index d2094abf3c2..e4efeb14afa 100644 --- a/src/azure/Integration/NServiceBus.Integration.Azure/AzureAppender.cs +++ b/src/azure/Integration/NServiceBus.Integration.Azure/AzureAppender.cs @@ -61,6 +61,8 @@ private void ConfigureThreshold() private void ConfigureAzureDiagnostics() { + if (!RoleEnvironment.IsAvailable) return; + Trace.Listeners.Add(new DiagnosticMonitorTraceListener()); var cloudStorageAccount = CloudStorageAccount.Parse(GetConnectionString()); diff --git a/src/azure/Integration/NServiceBus.Integration.Azure/AzureConfigurationSource.cs b/src/azure/Integration/NServiceBus.Integration.Azure/AzureConfigurationSource.cs index ab067e700fc..a411429886b 100644 --- a/src/azure/Integration/NServiceBus.Integration.Azure/AzureConfigurationSource.cs +++ b/src/azure/Integration/NServiceBus.Integration.Azure/AzureConfigurationSource.cs @@ -21,7 +21,7 @@ T IConfigurationSource.GetConfiguration() var sectionName = typeof(T).Name; var section = GetConfigurationHandler() - .GetSection(sectionName) as T ?? new T(); + .GetSection(sectionName) as T; foreach (var property in typeof(T).GetProperties().Where(x => x.DeclaringType == typeof(T))) { @@ -29,6 +29,8 @@ T IConfigurationSource.GetConfiguration() if (!string.IsNullOrEmpty(setting)) { + if( section == null) section = new T(); + property.SetValue(section, Convert.ChangeType(setting, property.PropertyType), null); } } diff --git a/src/hosting/NServiceBus.Hosting.Azure.HostProcess/Arguments/HostArguments.cs b/src/hosting/NServiceBus.Hosting.Azure.HostProcess/Arguments/HostArguments.cs new file mode 100644 index 00000000000..c1a8a57caec --- /dev/null +++ b/src/hosting/NServiceBus.Hosting.Azure.HostProcess/Arguments/HostArguments.cs @@ -0,0 +1,44 @@ +using System.Linq; +using Topshelf.Internal; +using Topshelf.Internal.ArgumentParsing; + +namespace NServiceBus.Hosting.Azure.HostProcess +{ + internal class HostArguments + { + public HostArguments(Parser.Args arguments) + { + Help = GetArgument(arguments, "help") ?? GetArgument(arguments, "?"); + ServiceName = GetArgument(arguments, "serviceName"); + DisplayName = GetArgument(arguments, "displayName"); + Description = GetArgument(arguments, "description"); + EndpointConfigurationType = GetArgument(arguments, "endpointConfigurationType"); + DependsOn = GetArgument(arguments, "dependsOn"); + StartManually = GetArgument(arguments, "startManually"); + Username = GetArgument(arguments, "username"); + Password = GetArgument(arguments, "password"); + } + + public IArgument Help { get; set; } + public IArgument ServiceName { get; set; } + public IArgument DisplayName { get; set; } + public IArgument Description { get; set; } + public IArgument EndpointConfigurationType { get; set; } + public IArgument DependsOn { get; set; } + public IArgument StartManually { get; set; } + public IArgument Username { get; set; } + public IArgument Password { get; set; } + + private static IArgument GetArgument(Parser.Args arguments, string key) + { + IArgument argument = arguments.CustomArguments.Where(x => x.Key != null).SingleOrDefault(x => x.Key.ToUpper() == key.ToUpper()); + + if (argument != null) + { + arguments.CustomArguments = arguments.CustomArguments.Except(new[] {argument}); + } + + return argument; + } + } +} \ No newline at end of file diff --git a/src/hosting/NServiceBus.Hosting.Azure.HostProcess/Content/Help.txt b/src/hosting/NServiceBus.Hosting.Azure.HostProcess/Content/Help.txt new file mode 100644 index 00000000000..02c3e71b936 --- /dev/null +++ b/src/hosting/NServiceBus.Hosting.Azure.HostProcess/Content/Help.txt @@ -0,0 +1,54 @@ +NServiceBus Message Endpoint Host Service + +USAGE: + NServiceBus.Hosting.Azure.HostProcess.exe [/install [/serviceName] + [/displayName] + [/description] + [/endpointConfigurationType] + [/instance] + [/startManually] + [/username] + [/password]] | + [/uninstall[/serviceName] + [/instance]] + +OPTIONS: + +/install Install the message endpoint as a Windows service +/serviceName Specify the service name for the installed service +/displayName Friendly name for the installed service +/description Description for the service +/endpointConfigurationType Specify the type implementing IConfigureThisEndpoint that should be used +/instance Specify the unique name of the service instance you wish to install + ex: "NServiceBus.Host.exe /install /instance:Instance5" +/startManually Specifies that the service should start manually +/username Username for the account the service should run under +/password Password for the service account + +If no service name is specified NServiceBus will use the full name of the +endpoint configuration type (that which implements NServiceBus.IConfigureThisEndpoint) +along with the version number of the assembly it is contained within, for example: + + MyPublisher.Endpoint_v1.0.0.0 + +The default for the display name is the same value as the service name, and the description +defaults to a generic NServiceBus host description. + +You can also specify the endpoint configuration type in the file NServiceBus.Host.exe.config. +This file is optional. + +If you don't specify the endpoint configuration type either in the command-line or in the +NServiceBus.Host.exe.config file, all the DLLs in the runtime directory will be scanned +for a type that implements NServiceBus.IConfigureThisEndpoint. + +If you set the service name and/or instance name during installation you will need to specify +them when uninstalling them as well, ex: + + NServiceBus.Host.exe /uninstall /serviceName:"MyPublisher" /instance:Instance89 + +EXAMPLES: + NServiceBus.Host.exe /install /serviceName:"MyPublisher" /displayName:"My Publisher Service" + /description:"Service for publishing event messages" + /endpointConfigurationType:"YourEndpointConfigType.YourNameSpace, YourAssembly" + /username:"corp\serviceuser" + /password:"p@ssw0rd!" NServiceBus.Production diff --git a/src/hosting/NServiceBus.Hosting.Azure.HostProcess/HostServiceLocator.cs b/src/hosting/NServiceBus.Hosting.Azure.HostProcess/HostServiceLocator.cs new file mode 100644 index 00000000000..e9c6629c350 --- /dev/null +++ b/src/hosting/NServiceBus.Hosting.Azure.HostProcess/HostServiceLocator.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using Microsoft.Practices.ServiceLocation; + +namespace NServiceBus.Hosting.Azure.HostProcess +{ + /// + /// Plugs into the generic service locator to return an instance of . + /// + public class HostServiceLocator : ServiceLocatorImplBase + { + /// + /// Command line arguments. + /// + public static string[] Args; + + /// + /// Returns an instance of + /// + /// + /// + /// + protected override object DoGetInstance(Type serviceType, string key) + { + var endpoint = Type.GetType(key,true); + return new WindowsHost(endpoint, Args); + } + + /// + /// Not implemented. + /// + /// + /// + protected override IEnumerable DoGetAllInstances(Type serviceType) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/src/hosting/NServiceBus.Hosting.Azure.HostProcess/NServiceBus.Hosting.Azure.HostProcess.csproj b/src/hosting/NServiceBus.Hosting.Azure.HostProcess/NServiceBus.Hosting.Azure.HostProcess.csproj new file mode 100644 index 00000000000..2589fb73fe9 --- /dev/null +++ b/src/hosting/NServiceBus.Hosting.Azure.HostProcess/NServiceBus.Hosting.Azure.HostProcess.csproj @@ -0,0 +1,97 @@ + + + + Debug + x86 + 8.0.30703 + 2.0 + {11B81F23-64C6-4341-94AC-38B3C4C6B1E7} + Exe + Properties + NServiceBus.Hosting.Azure.HostProcess + NServiceBus.Hosting.Azure.HostProcess + v4.0 + + + 512 + + + x86 + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + x86 + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\..\..\lib\log4net.dll + + + ..\..\..\lib\Topshelf\Magnum.dll + + + ..\..\..\lib\ServiceLocation\Microsoft.Practices.ServiceLocation.dll + + + ..\..\..\build\output\NServiceBus.dll + + + ..\..\..\build\output\NServiceBus.Azure.dll + + + ..\..\..\build\output\NServiceBus.Core.dll + + + + + + + + + ..\..\..\lib\Topshelf\Topshelf.dll + + + + + CommonAssemblyInfo.cs + + + + + + + + + + {6591ED91-F9A1-4CC3-813E-A33E07439D49} + NServiceBus.Hosting.Azure + + + {B5F333D1-D6B1-49FB-82F6-74641E5D11E3} + NServiceBus.Hosting + + + + + + + + \ No newline at end of file diff --git a/src/hosting/NServiceBus.Hosting.Azure.HostProcess/Program.cs b/src/hosting/NServiceBus.Hosting.Azure.HostProcess/Program.cs new file mode 100644 index 00000000000..921f282ce72 --- /dev/null +++ b/src/hosting/NServiceBus.Hosting.Azure.HostProcess/Program.cs @@ -0,0 +1,222 @@ +using System; +using System.Collections.Generic; +using System.Configuration; +using System.IO; +using System.Linq; +using System.Reflection; +using NServiceBus.Hosting.Helpers; +using Topshelf; +using Topshelf.Configuration; +using Topshelf.Internal; + +namespace NServiceBus.Hosting.Azure.HostProcess +{ + class Program + { + private static void Main(string[] args) + { + Parser.Args commandLineArguments = Parser.ParseArgs(args); + var arguments = new HostArguments(commandLineArguments); + + if (arguments.Help != null) + { + DisplayHelpContent(); + + return; + } + + Type endpointConfigurationType = GetEndpointConfigurationType(arguments); + + AssertThatEndpointConfigurationTypeHasDefaultConstructor(endpointConfigurationType); + + string endpointConfigurationFile = GetEndpointConfigurationFile(endpointConfigurationType); + + var endpointConfiguration = Activator.CreateInstance(endpointConfigurationType); + + EndpointId = GetEndpointId(endpointConfiguration); + + AppDomain.CurrentDomain.SetupInformation.AppDomainInitializerArguments = args; + + IRunConfiguration cfg = RunnerConfigurator.New(x => + { + x.ConfigureServiceInIsolation(endpointConfigurationType.AssemblyQualifiedName, c => + { + c.ConfigurationFile(endpointConfigurationFile); + c.CommandLineArguments(args, () => SetHostServiceLocatorArgs); + c.WhenStarted(service => service.Start()); + c.WhenStopped(service => service.Stop()); + c.CreateServiceLocator(() => new HostServiceLocator()); + }); + + if (arguments.Username != null && arguments.Password != null) + { + x.RunAs(arguments.Username.Value, arguments.Password.Value); + } + else + { + x.RunAsLocalSystem(); + } + + if (arguments.StartManually != null) + { + x.DoNotStartAutomatically(); + } + + x.SetDisplayName(arguments.DisplayName != null ? arguments.DisplayName.Value : EndpointId); + x.SetServiceName(arguments.ServiceName != null ? arguments.ServiceName.Value : EndpointId); + x.SetDescription(arguments.Description != null ? arguments.Description.Value : "NServiceBus Message Endpoint Host Service"); + x.DependencyOnMsmq(); + + var serviceCommandLine = commandLineArguments.CustomArguments.AsCommandLine(); + + if (arguments.ServiceName != null) + { + serviceCommandLine += " /serviceName:\"" + arguments.ServiceName.Value + "\""; + } + + x.SetServiceCommandLine(serviceCommandLine); + + if (arguments.DependsOn != null) + { + var dependencies = arguments.DependsOn.Value.Split(','); + + foreach (var dependency in dependencies) + { + if (dependency.ToUpper() == KnownServiceNames.Msmq) + { + continue; + } + + x.DependsOn(dependency); + } + } + }); + + Runner.Host(cfg, args); + } + + private static void DisplayHelpContent() + { + try + { + var stream = Assembly.GetCallingAssembly().GetManifestResourceStream("NServiceBus.Hosting.Windows.Content.Help.txt"); + + if (stream != null) + { + var helpText = new StreamReader(stream).ReadToEnd(); + + Console.WriteLine(helpText); + } + } + catch (Exception e) + { + Console.WriteLine(e); + } + } + + /// + /// Gives an identifier for this endpoint + /// + public static string EndpointId { get; set; } + + private static void SetHostServiceLocatorArgs(string[] args) + { + HostServiceLocator.Args = args; + } + + private static void AssertThatEndpointConfigurationTypeHasDefaultConstructor(Type type) + { + var constructor = type.GetConstructor(Type.EmptyTypes); + + if (constructor == null) + throw new InvalidOperationException("Endpoint configuration type needs to have a default constructor: " + type.FullName); + } + + private static string GetEndpointConfigurationFile(Type endpointConfigurationType) + { + return Path.Combine( + AppDomain.CurrentDomain.BaseDirectory, + endpointConfigurationType.Assembly.ManifestModule.Name + ".config"); + } + + /// + /// Gives a string which serves to identify the endpoint. + /// + /// + /// + public static string GetEndpointId(object endpointConfiguration) + { + string endpointName = endpointConfiguration.GetType().FullName; + return string.Format("{0}_v{1}", endpointName, endpointConfiguration.GetType().Assembly.GetName().Version); + } + + private static Type GetEndpointConfigurationType(HostArguments arguments) + { + if (arguments.EndpointConfigurationType != null) + { + string t = arguments.EndpointConfigurationType.Value; + if (t != null) + { + Type endpointType = Type.GetType(t, false); + if (endpointType == null) + throw new ConfigurationErrorsException(string.Format("Command line argument 'endpointConfigurationType' has specified to use the type '{0}' but that type could not be loaded.", t)); + + return endpointType; + } + } + + string endpoint = ConfigurationManager.AppSettings["EndpointConfigurationType"]; + if (endpoint != null) + { + var endpointType = Type.GetType(endpoint, false); + if (endpointType == null) + throw new ConfigurationErrorsException(string.Format("The 'EndpointConfigurationType' entry in the NServiceBus.Host.exe.config has specified to use the type '{0}' but that type could not be loaded.", endpoint)); + + return endpointType; + } + + IEnumerable endpoints = ScanAssembliesForEndpoints(); + + ValidateEndpoints(endpoints); + + return endpoints.First(); + } + + private static IEnumerable ScanAssembliesForEndpoints() + { + foreach (var assembly in AssemblyScanner.GetScannableAssemblies()) + foreach (Type type in assembly.GetTypes().Where( + t => typeof(IConfigureThisEndpoint).IsAssignableFrom(t) + && t != typeof(IConfigureThisEndpoint) + && !t.IsAbstract)) + { + yield return type; + } + } + + private static void ValidateEndpoints(IEnumerable endpointConfigurationTypes) + { + if (endpointConfigurationTypes.Count() == 0) + { + throw new InvalidOperationException("No endpoint configuration found in scanned assemlies. " + + "This usually happens when NServiceBus fails to load your assembly contaning IConfigureThisEndpoint." + + " Try specifying the type explicitly in the NServiceBus.Host.exe.config using the appsetting key: EndpointConfigurationType, " + + "Scanned path: " + AppDomain.CurrentDomain.BaseDirectory); + } + + if (endpointConfigurationTypes.Count() > 1) + { + throw new InvalidOperationException("Host doesn't support hosting of multiple endpoints. " + + "Endpoint classes found: " + + string.Join(", ", + endpointConfigurationTypes.Select( + e => e.AssemblyQualifiedName).ToArray()) + + " You may have some old assemblies in your runtime directory." + + " Try right-clicking your VS project, and selecting 'Clean'." + ); + + } + } + + } +} diff --git a/src/hosting/NServiceBus.Hosting.Azure.HostProcess/Properties/AssemblyInfo.cs b/src/hosting/NServiceBus.Hosting.Azure.HostProcess/Properties/AssemblyInfo.cs new file mode 100644 index 00000000000..d8badad87c8 --- /dev/null +++ b/src/hosting/NServiceBus.Hosting.Azure.HostProcess/Properties/AssemblyInfo.cs @@ -0,0 +1,23 @@ +#region License + +/* + * See CommonAssemblyInfo under /src for copyright information. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using System.Reflection; +[assembly: AssemblyTitle("Generic Host Process for running NServiceBus in a child process on windows azeure")] +[assembly: AssemblyDescription("Functionality for generically hosting nServiceBus processes.")] \ No newline at end of file diff --git a/src/hosting/NServiceBus.Hosting.Azure.HostProcess/WindowsHost.cs b/src/hosting/NServiceBus.Hosting.Azure.HostProcess/WindowsHost.cs new file mode 100644 index 00000000000..2b1d2df8b98 --- /dev/null +++ b/src/hosting/NServiceBus.Hosting.Azure.HostProcess/WindowsHost.cs @@ -0,0 +1,45 @@ +using System; + +namespace NServiceBus.Hosting.Azure.HostProcess +{ + /// + /// A windows implementation of the NServiceBus hosting solution + /// + public class WindowsHost : MarshalByRefObject + { + private readonly GenericHost genericHost; + + /// + /// Accepts the type which will specify the users custom configuration. + /// This type should implement . + /// + /// + /// + public WindowsHost(Type endpointType, string[] args) + { + var specifier = (IConfigureThisEndpoint)Activator.CreateInstance(endpointType); + + Program.EndpointId = Program.GetEndpointId(specifier); + + genericHost = new GenericHost(specifier, args, new[] { typeof(Development) }); + } + + /// + /// Does startup work. + /// + public void Start() + { + genericHost.Start(); + } + + /// + /// Does shutdown work. + /// + public void Stop() + { + genericHost.Stop(); + } + + + } +} \ No newline at end of file diff --git a/src/hosting/NServiceBus.Hosting.Azure/DynamicHost/DynamicEndpointLoader.cs b/src/hosting/NServiceBus.Hosting.Azure/DynamicHost/DynamicEndpointLoader.cs new file mode 100644 index 00000000000..3940f574cd4 --- /dev/null +++ b/src/hosting/NServiceBus.Hosting.Azure/DynamicHost/DynamicEndpointLoader.cs @@ -0,0 +1,26 @@ +using System.Collections.Generic; +using System.Linq; +using Microsoft.WindowsAzure; +using Microsoft.WindowsAzure.StorageClient; + +namespace NServiceBus.Hosting +{ + internal class DynamicEndpointLoader + { + private readonly CloudBlobClient client; + + public DynamicEndpointLoader() + { + var storageAccount = CloudStorageAccount.Parse("UseDevelopmentStorage=true"); + client = storageAccount.CreateCloudBlobClient(); + } + + public IEnumerable LoadEndpoints() + { + var blobContainer = client.GetContainerReference("endpoints"); + return from b in blobContainer.ListBlobs() + where b.Uri.AbsolutePath.EndsWith(".zip") + select new EndpointToHost((CloudBlockBlob)b) ; + } + } +} \ No newline at end of file diff --git a/src/hosting/NServiceBus.Hosting.Azure/DynamicHost/DynamicEndpointProvisioner.cs b/src/hosting/NServiceBus.Hosting.Azure/DynamicHost/DynamicEndpointProvisioner.cs new file mode 100644 index 00000000000..2e89c8d3a35 --- /dev/null +++ b/src/hosting/NServiceBus.Hosting.Azure/DynamicHost/DynamicEndpointProvisioner.cs @@ -0,0 +1,26 @@ +using System.Collections.Generic; +using System.IO; +using Microsoft.WindowsAzure.ServiceRuntime; + +namespace NServiceBus.Hosting +{ + internal class DynamicEndpointProvisioner + { + public IEnumerable Provision(IEnumerable toHost) + { + var localResource = RoleEnvironment.GetLocalResource("endpoints"); + + foreach (var assemblies in toHost) + { + assemblies.ExtractTo(localResource.RootPath); + + yield return new ServiceToRun + { + EntryPoint = Path.Combine(localResource.RootPath, assemblies.EndpointName, "NServiceBus.Hosting.Azure.HostProcess.exe"), + ServiceName = assemblies.EndpointName + }; + } + + } + } +} \ No newline at end of file diff --git a/src/hosting/NServiceBus.Hosting.Azure/DynamicHost/DynamicEndpointStarter.cs b/src/hosting/NServiceBus.Hosting.Azure/DynamicHost/DynamicEndpointStarter.cs new file mode 100644 index 00000000000..a34f30be24c --- /dev/null +++ b/src/hosting/NServiceBus.Hosting.Azure/DynamicHost/DynamicEndpointStarter.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; +using System.Diagnostics; + +namespace NServiceBus.Hosting +{ + internal class DynamicEndpointStarter + { + public DynamicEndpointStarter() + { + } + + public void Start(IEnumerable toHost) + { + foreach(var service in toHost) + { + var processStartInfo = new ProcessStartInfo(service.EntryPoint, + //"/install " + + "/serviceName:\"" + service.ServiceName + + "\" /displayName:\"" + service.ServiceName + + "\" /description:\"" + service.ServiceName + "\""); + Process.Start(processStartInfo); + + } + + } + } +} \ No newline at end of file diff --git a/src/hosting/NServiceBus.Hosting.Azure/DynamicHost/DynamicHostController.cs b/src/hosting/NServiceBus.Hosting.Azure/DynamicHost/DynamicHostController.cs new file mode 100644 index 00000000000..ac949f4c023 --- /dev/null +++ b/src/hosting/NServiceBus.Hosting.Azure/DynamicHost/DynamicHostController.cs @@ -0,0 +1,71 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Reflection; +using NServiceBus.Config; +using NServiceBus.Hosting.Configuration; +using NServiceBus.Hosting.Helpers; +using NServiceBus.Hosting.Profiles; +using NServiceBus.Integration.Azure; +using NServiceBus.ObjectBuilder; + +namespace NServiceBus.Hosting +{ + public class DynamicHostController : IHost + { + private IConfigureThisEndpoint specifier; + private ConfigManager configManager; + private ProfileManager profileManager; + + public DynamicHostController(IConfigureThisEndpoint specifier, string[] requestedProfiles,IEnumerable defaultProfiles) + { + this.specifier = specifier; + + var assembliesToScan = new[] {GetType().Assembly}; + + profileManager = new ProfileManager(assembliesToScan, specifier, requestedProfiles, defaultProfiles); + configManager = new ConfigManager(assembliesToScan, specifier); + } + + public void Start() + { + Configure + .With(GetType().Assembly) + .DefaultBuilder() + .AzureConfigurationSource(); + + Configure.Instance.Configurer.ConfigureComponent(DependencyLifecycle.SingleInstance); + Configure.Instance.Configurer.ConfigureComponent(DependencyLifecycle.SingleInstance); + Configure.Instance.Configurer.ConfigureComponent(DependencyLifecycle.SingleInstance); + + var loader = Configure.Instance.Builder.Build(); + var provisioner = Configure.Instance.Builder.Build(); + var starter = Configure.Instance.Builder.Build(); + + var endpointsToHost = loader.LoadEndpoints(); + var servicesToRun = provisioner.Provision(endpointsToHost); + starter.Start(servicesToRun); + + + //if(IsRunningInAzure()) + //{ + // // start monitoring the generic host + // // only monitor on azure itself and when profile is production + //} + } + + public void Stop() + { + // stop every host + + // remove the assemblies + } + + //private bool IsRunningInAzure() + //{ + // return RoleEnvironment.IsAvailable && !RoleEnvironment.DeploymentId.StartsWith("deployment("); + //} + + + } +} diff --git a/src/hosting/NServiceBus.Hosting.Azure/DynamicHost/EndpointToHost.cs b/src/hosting/NServiceBus.Hosting.Azure/DynamicHost/EndpointToHost.cs new file mode 100644 index 00000000000..719cbc33b1d --- /dev/null +++ b/src/hosting/NServiceBus.Hosting.Azure/DynamicHost/EndpointToHost.cs @@ -0,0 +1,34 @@ +using System.IO; +using Ionic.Zip; +using Microsoft.WindowsAzure.StorageClient; + +namespace NServiceBus.Hosting +{ + internal class EndpointToHost + { + private readonly CloudBlockBlob blob; + + public EndpointToHost(CloudBlockBlob blob) + { + this.blob = blob; + EndpointName = Path.GetFileNameWithoutExtension(blob.Uri.AbsolutePath); + } + + public string EndpointName { get; private set; } + + public string ExtractTo(string rootPath) + { + var localDirectory = Path.Combine(rootPath, EndpointName); + var localFileName = Path.Combine(rootPath, Path.GetFileName(blob.Uri.AbsolutePath)); + + blob.DownloadToFile(localFileName); + + using(var zip = new ZipFile(localFileName)) + { + zip.ExtractAll(localDirectory, ExtractExistingFileAction.OverwriteSilently); + } + + return localDirectory; + } + } +} \ No newline at end of file diff --git a/src/hosting/NServiceBus.Hosting.Azure/DynamicHost/ServiceToRun.cs b/src/hosting/NServiceBus.Hosting.Azure/DynamicHost/ServiceToRun.cs new file mode 100644 index 00000000000..927b0c508d0 --- /dev/null +++ b/src/hosting/NServiceBus.Hosting.Azure/DynamicHost/ServiceToRun.cs @@ -0,0 +1,9 @@ +namespace NServiceBus.Hosting +{ + internal class ServiceToRun + { + public string EntryPoint { get; set; } + + public string ServiceName { get; set; } + } +} \ No newline at end of file diff --git a/src/hosting/NServiceBus.Hosting.Azure/HttpApplication.cs b/src/hosting/NServiceBus.Hosting.Azure/HttpApplication.cs new file mode 100644 index 00000000000..02791d8ca52 --- /dev/null +++ b/src/hosting/NServiceBus.Hosting.Azure/HttpApplication.cs @@ -0,0 +1,103 @@ +using System; +using System.Collections.Generic; +using System.Configuration; +using System.Linq; +using NServiceBus.Hosting.Helpers; +using NServiceBus.Integration.Azure; + +namespace NServiceBus.Hosting.Azure +{ + public class HttpApplication : System.Web.HttpApplication + { + private static readonly Lazy StartGenericHost = new Lazy(StartHost); + + private const string ProfileSetting = "NServiceBus.Profile"; + private GenericHost genericHost; + + private static GenericHost StartHost() + { + Configure.WithWeb(); + + var azureSettings = new AzureConfigurationSettings(); + var requestedProfiles = azureSettings.GetSetting(ProfileSetting); + + var endpointConfigurationType = GetEndpointConfigurationType(azureSettings); + + AssertThatEndpointConfigurationTypeHasDefaultConstructor(endpointConfigurationType); + + var specifier = (IConfigureThisEndpoint)Activator.CreateInstance(endpointConfigurationType); + + var genericHost = new GenericHost(specifier, requestedProfiles.Split(' '), null); + + genericHost.Start(); + + return genericHost; + } + + private static void AssertThatEndpointConfigurationTypeHasDefaultConstructor(Type type) + { + var constructor = type.GetConstructor(Type.EmptyTypes); + + if (constructor == null) + throw new InvalidOperationException("Endpoint configuration type needs to have a default constructor: " + type.FullName); + } + + private static Type GetEndpointConfigurationType(AzureConfigurationSettings settings) + { + string endpoint = settings.GetSetting("EndpointConfigurationType"); + if (!String.IsNullOrEmpty(endpoint)) + { + var endpointType = Type.GetType(endpoint, false); + if (endpointType == null) + throw new ConfigurationErrorsException(string.Format("The 'EndpointConfigurationType' entry in the role config has specified to use the type '{0}' but that type could not be loaded.", endpoint)); + + return endpointType; + } + + IEnumerable endpoints = ScanAssembliesForEndpoints(); + + ValidateEndpoints(endpoints); + + return endpoints.First(); + } + + private static IEnumerable ScanAssembliesForEndpoints() + { + return AssemblyScanner.GetScannableAssemblies().SelectMany( + assembly => assembly.GetTypes().Where( + t => typeof(IConfigureThisEndpoint).IsAssignableFrom(t) + && t != typeof(IConfigureThisEndpoint) + && !t.IsAbstract)); + } + + private static void ValidateEndpoints(IEnumerable endpointConfigurationTypes) + { + if (endpointConfigurationTypes.Count() == 0) + { + throw new InvalidOperationException("No endpoint configuration found in scanned assemlies. " + + "This usually happens when NServiceBus fails to load your assembly containing IConfigureThisEndpoint." + + " Try specifying the type explicitly in the roles config using the appsetting key: EndpointConfigurationType, " + + "Scanned path: " + AppDomain.CurrentDomain.BaseDirectory); + } + + if (endpointConfigurationTypes.Count() > 1) + { + throw new InvalidOperationException("Host doesn't support hosting of multiple endpoints. " + + "Endpoint classes found: " + + string.Join(", ", + endpointConfigurationTypes.Select( + e => e.AssemblyQualifiedName).ToArray()) + + " You may have some old assemblies in your runtime directory." + + " Try right-clicking your VS project, and selecting 'Clean'." + ); + + } + } + + protected void Application_BeginRequest(object sender, EventArgs e) + { + genericHost = StartGenericHost.Value; + } + + } +} diff --git a/src/hosting/NServiceBus.Hosting.Azure/LoggingHandlers/DevelopmentLoggingHandler.cs b/src/hosting/NServiceBus.Hosting.Azure/LoggingHandlers/DevelopmentLoggingHandler.cs new file mode 100644 index 00000000000..27560c98cc6 --- /dev/null +++ b/src/hosting/NServiceBus.Hosting.Azure/LoggingHandlers/DevelopmentLoggingHandler.cs @@ -0,0 +1,13 @@ +namespace NServiceBus.Hosting.Azure.LoggingHandlers +{ + /// + /// Handles logging configuration for the development profile + /// + public class DevelopmentLoggingHandler : IConfigureLoggingForProfile + { + void IConfigureLogging.Configure(IConfigureThisEndpoint specifier) + { + // startup logging is handled outside nsb by wadcfg file + } + } +} \ No newline at end of file diff --git a/src/hosting/NServiceBus.Hosting.Azure/LoggingHandlers/ProductionLoggingHandler.cs b/src/hosting/NServiceBus.Hosting.Azure/LoggingHandlers/ProductionLoggingHandler.cs index b0633ca357e..647c7cf423e 100644 --- a/src/hosting/NServiceBus.Hosting.Azure/LoggingHandlers/ProductionLoggingHandler.cs +++ b/src/hosting/NServiceBus.Hosting.Azure/LoggingHandlers/ProductionLoggingHandler.cs @@ -1,7 +1,4 @@ -using NServiceBus.Config; -using NServiceBus.Integration.Azure; - -namespace NServiceBus.Hosting.Azure.LoggingHandlers +namespace NServiceBus.Hosting.Azure.LoggingHandlers { /// /// Handles logging configuration for the production profile @@ -10,16 +7,7 @@ public class ProductionLoggingHandler : IConfigureLoggingForProfile { void IConfigureLogging.Configure(IConfigureThisEndpoint specifier) { - //if (Configure.Instance == null || Configure.Instance.Configurer == null) - // return; - - //Configure.Instance - // .AzureConfigurationSource() - // .Log4Net( - // a => - // { - // a.ScheduledTransferPeriod = 10; - // }); + // startup logging is handled outside nsb by wadcfg file } } } \ No newline at end of file diff --git a/src/hosting/NServiceBus.Hosting.Azure/NServiceBus.Hosting.Azure.csproj b/src/hosting/NServiceBus.Hosting.Azure/NServiceBus.Hosting.Azure.csproj index 82a56cfd1c7..537570d5f9c 100644 --- a/src/hosting/NServiceBus.Hosting.Azure/NServiceBus.Hosting.Azure.csproj +++ b/src/hosting/NServiceBus.Hosting.Azure/NServiceBus.Hosting.Azure.csproj @@ -48,6 +48,9 @@ False ..\..\..\build\NServiceBus.Azure\Iesi.Collections.dll + + ..\..\..\lib\Ionic.Zip.dll + False ..\..\..\build\NServiceBus.Azure\LinFu.DynamicProxy.dll @@ -99,16 +102,28 @@ - + + CommonAssemblyInfo.cs + + + + + + + + + + + - - + + - + @@ -116,7 +131,6 @@ NServiceBus.Hosting -