Monday, March 12, 2012

MultiAppLauncher

The only way to handle repetitive tasks, are to automate them. After getting fed up starting n cmd consoles and n "Set Startup Projects - Multiple Startup Projects" in Visual Studio, it was time to do something, and this is the result.

This application is a small helper app I wrote for starting multiple NServiceBus services for a given profile. It will monitor the process status of the host and show it in the GUI

Features

  • Load and save the current application set to a xml file
  • Hides the console application from the taskbar
  • Double click an item in the list to bring the process to front
  • Kill all processes (from menu)
  • Modify profile for a given application (right click the application)

You can get the source from GitHub: https://github.com/mteinum/MultiAppLauncher

settings file

The application supports Drag & Drop so you can drag the NServiceBus.Host.exe file on the application and you we will be asked for a profile to be assigned.

The settings file for an applicationset is an array of filenames to execute, and a profile that will be passed as an argument.

<?xml version="1.0"?>
<SettingsDocument xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
                  xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <FileNames>
    <FileSettings>
      <Name>C:\dev\MyService1\bin\Debug\NServiceBus.Host.exe</Name>
      <Profile>NServiceBus.Lite</Profile>
    </FileSettings>
    ....
  </FileNames>
</SettingsDocument>

Shortcuts

CTRL+OOpen an application set file
CTRL+SSave the current configuration
CTRL+RStart all applications that are not running. If no rows are selected then all the applications will be started. If you do explicit selection, only those selected will be started.

Runtime

The application requires .NET 4.0 installed. http://www.microsoft.com/download/en/details.aspx?id=17851

Binaries

Latest version: https://github.com/mteinum/MultiAppLauncher/downloads

Saturday, March 10, 2012

NServiceBus 3.0 - SMTP Transport

NServiceBus 3.0 is fresh out of GitHub, and I'm wading through the code and testing out different concepts. You might learn something from this code (the code in this blog post ;), but its far from production stable.

SmtpClient

With NServiceBus 3.0 there is a FtpTransport that enable us to send/receive messages over FTP instead of MSMQ. RFC 2549 is not supported, but before we start with that, lets try something simpler: Send messages using SMTP.

Looking at the code for FtpMessageQueue, ISendMessages and IReceiveMessages are the interfaces that NServiceBus uses for sending and receiving TransportMessages.

.NET includes a SmtpClient, but no POP3 support, so we will now limit ourself to sending messages. Let's start.

The preferred way I like to host NServiceBus applications, are using the NServiceBus.Host.exe. This host has the concept of one input queue and one error queue. At the moment we will not process any input messages (in lack of an pop3 client), but we will send out messages.

Message

It could be a message that would launch a nuclear bomb, brew coffee or buy a movie ticket, but for the example let's use:

public class MyMessage : IMessage
{
  public string Foo;
  public string Bar;
}

IWantToRunAtStartup

There are various ways to interact with the Host, IWantToRunAtStartup is an interface you can decoracte a class with and it will be called at startup. We'll combine the marker interface IConfigureThisEndpoint and IWantCustomInitialization that will setup the smtptransport.

public class Runner :
             IConfigureThisEndpoint,
             IWantToRunAtStartup,
             IWantCustomInitialization
{
  public IBus Bus { get; set; }

  public void Run()
  {
    string line;

    while ((line = Console.ReadLine()) != null)
    {
      Bus.Send(new MyMessage { Bar = "bar", Foo = "foo" });
    }
  }

  public void Stop()
  {
  }

  public void Init()
  {
    Configure
     .With()
     .DefaultBuilder()
     .SmtpTransport()
     .UnicastBus()
     .SendOnly();
    }
}

App.config

The config file is the preferred place to put any configuration that will affect the application; connectionstrings, paths, ....

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="SmtpQueueConfig"
              type="SmtpTransport.SmtpQueueConfig, SmtpTransport" />
    <section name="UnicastBusConfig"
              type="NServiceBus.Config.UnicastBusConfig, NServiceBus.Core" />
    <section name="MessageForwardingInCaseOfFaultConfig"
              type="NServiceBus.Config.MessageForwardingInCaseOfFaultConfig, NServiceBus.Core" />
  </configSections>

  <MessageForwardingInCaseOfFaultConfig ErrorQueue="errors"/>

  <SmtpQueueConfig
      SmtpServer = "smtp.gmail.com"
      Port       = "587"
      EnableSsl  = "true"
      From       = "username@gmail.com"
      UserName   = "username@gmail.com"
      Password   = ""/>

  <UnicastBusConfig>
    <MessageEndpointMappings>
      <add Messages="SmtpTransport" Endpoint="username@gmail.com" />
    </MessageEndpointMappings>
  </UnicastBusConfig>

</configuration>

UnicastBusConfig and MessageForwardingInCaseOfFaultConfig is part of NServiceBus, SmtpQueueConfig is created by me and looks like:

public class SmtpQueueConfig : ConfigurationSection
{
  [ConfigurationProperty("SmtpServer", IsRequired = true)]
  public string SmtpServer
  {
    get { return this["SmtpServer"].ToString(); }
    set { this["SmtpServer"] = value; }
  }

  [ConfigurationProperty("Port", IsRequired = true)]
  public int Port
  {
    get { return (int)this["Port"]; }
    set { this["Port"] = value; }
  }

  [ConfigurationProperty("EnableSsl", IsRequired = true)]
  public bool EnableSsl
  {
    get { return (bool)this["EnableSsl"]; }
    set { this["EnableSsl"] = value; }
  }

  [ConfigurationProperty("From", IsRequired = true)]
  public string From
  {
    get { return this["From"].ToString(); }
    set { this["From"] = value; }
  }

  [ConfigurationProperty("UserName", IsRequired = true)]
  public string UserName
  {
    get { return this["UserName"].ToString(); }
    set { this["UserName"] = value; }
  }

  [ConfigurationProperty("Password", IsRequired = true)]
  public string Password
  {
    get { return this["Password"].ToString(); }
    set { this["Password"] = value; }
  }
}

No big surprises here. If you now goes back to the Init method of the Runner class you will see that we call .SmtpTransport() on the Configure class. This is an extension method:

public static class ConfigureSmtpQueue
{
  public static Configure SmtpTransport(this Configure config)
  {
    var smtpQueue = config.Configurer.ConfigureComponent<SmtpMessageQueue>(
                                 DependencyLifecycle.SingleInstance);
    var cfg = Configure.GetConfigSection<SmtpQueueConfig>();

    if (cfg != null)
    {
      smtpQueue.ConfigureProperty(t => t.SmtpServer, cfg.SmtpServer);
      smtpQueue.ConfigureProperty(t => t.Port, cfg.Port);
      smtpQueue.ConfigureProperty(t => t.EnableSsl, cfg.EnableSsl);
      smtpQueue.ConfigureProperty(t => t.From, cfg.From);
      smtpQueue.ConfigureProperty(t => t.UserName, cfg.UserName);
      smtpQueue.ConfigureProperty(t => t.Password, cfg.Password);
    }

    return config;
  }
}

It's the binding between the configuration and our transport class. Then finally:

SmtpMessageQueue

public class SmtpMessageQueue : ISendMessages, IReceiveMessages
{
  public string SmtpServer { get; set; }
  public int Port { get; set; }
  public bool EnableSsl { get; set; }
  public string From { get; set; }
  public string UserName { get; set; }
  public string Password { get; set; }

  public void Send(TransportMessage message, Address address)
  {
    using (var client = new SmtpClient(SmtpServer, Port))
    {
      client.EnableSsl = EnableSsl;
      client.Credentials = new NetworkCredential(UserName, Password);

      var serializer = new JavaScriptSerializer();

      var mailMessage = new MailMessage(From, address.ToString())
                        {
                          Subject = "NServiceBus TransportMessage",
                          Body = serializer.Serialize(CreateMailMetaData(message));
                        };

      mailMessage.Attachments.Add(
        new Attachment(
          new MemoryStream(message.Body),
          "body.bin",
          "application/octet-stream"));

          client.Send(mailMessage);
        }
    }

  private MailMetaData CreateMailMetaData(TransportMessage message)
  {
    return new MailMetaData
    {
      CorrelationId = message.CorrelationId,
      Id = message.Id,
      IdForCorrelation = message.IdForCorrelation,
      ReplyToAddress = message.ReplyToAddress.ToString(),
      TimeToBeReceived = message.TimeToBeReceived,
      Header = message.Headers.Select(
        x => new HeaderPair
             {
               Name = x.Key,
               Value = x.Value }).ToList()
      };
  }

  public void Init(Address address, bool transactional)
  {
    // TODO: add a pop3 client and poll for new messages
  }

  public bool HasMessage()
  {
    return false;
  }

  public TransportMessage Receive()
  {
    return null;
  }
}

public class MailMetaData
{
  public string CorrelationId;
  public string Id;
  public string IdForCorrelation;
  public string ReplyToAddress;
  public TimeSpan TimeToBeReceived;
  public List<HeaderPair> Header;
}

public class HeaderPair
{
  public string Name;
  public string Value;
}

Does it work? This is the email in gmail:

Yes! We'll fix the pop3 / receiver next week.

NServiceBus 3.0

The coolest thing happening on the software front in 2012. More info on Udi's blog:

http://www.udidahan.com/2012/03/08/nservicebus-3-0-released/

Get the binaries: http://www.nservicebus.com, or the sourcecode https://github.com/NServiceBus/NServiceBus

RunMeFirst.bat

Run this as Administrator

Still no sight of RFC 2549...

Sunday, February 26, 2012