We had a Ajax-ified page break on us today and eventually figured out that the cause was related to an upgrade of jQuery and in particular a change in how jQuery 1.4 does serialization of array parameters for Ajax operations. From the jQuery 1.4 Release Notes.
jQuery 1.4 adds support for nested param serialization in jQuery.param, using the approach popularized by PHP, and supported by Ruby on Rails. For instance, {foo: ["bar", "baz"]} will be serialized as “foo[]=bar&foo[]=baz”.
In jQuery 1.3, {foo: ["bar", "baz"]} was serialized as “foo=bar&foo=baz”. However, there was no way to encode a single-element Array using this approach. If you need the old behavior, you can turn it back on by setting the traditional Ajax setting (globally via jQuery.ajaxSettings.traditional or on a case-by-case basis via the traditional flag).
Our page was using getJSON to get data back from an ASP.Net MVC action that looks like this
public JsonResult GetListElements(string listTitle, string[] levelValues)
We found that the second array parameter would always be null. Adding the recommended Ajax setting for the page corrected the issue.
// Enables for all serialization
jQuery.ajaxSettings.traditional = true;
For more details about this issue check out this post: jQuery 1.4 breaks ASP.NET MVC parameter posting.
Out of the box Dovetail Carrier is very good at receiving emails and allowing you create business functionality which can react to incoming messages. There is a flip side to receiving emails. Sending them. This post will explain how the Email Agent extension uses templates to send email notifications. First we need to explore what information your template will have available to it. We’ll start by setting the stage.
Real World Scenario
In the Email Agent extension. When an email is received by your company’s support account the New Case Support Email rule set is invoked. This rule set determines that the email is a request for support and a new case is created. The next rule action should send a response email. What should the email tell the user?
- It should inform them of details about the new case they just created.
- It would also be really nice to send them a hyperlink for viewing this case over at our self service web site.
These two things translate into the following bits of information.
- Which email address do we send the message to?
- The title of the new case.
- What is the database identifier. We’ll use it to link the customer to their new case on our self service site.
The good news is that luckily, and by luckily I mean by design, this information is already available to the send notification action via the RuleContext object.
Rule Context
The bits of information we need to communicate can be found in the rule context. Let’s take a look at how the information we need gets put into the rule context.
The rule context is important. It is the glue that ties all the conditions and actions together letting them communicate with each other by pushing state into the context or by finding state put into the context by previous rule actors.
The Omnipresent Message
The rule context will always have the current message being processed. In this case the message being processed is an IncomingEmailMessage whose From property contains the sender’s email address.
Create Case
The case details are pushed into the context by the action which creates the case. How do you know this? You have the source code. Review the previous actions and conditions to see what objects get pushed into the rule context.
public class CreateCase : IAction<IncomingEmailMessage>
{
//...
public void Execute(IRuleContext<IncomingEmailMessage> context)
{
var contact = context.Find<Contact>();
var title = context.Message.Subject;
Case caseCreated = _dovetailSdkToolkitAdapter.CreateCase(contact, title);
_logger.LogInfo("Case {0} created with title {1} for email {2}.", caseCreated.Id, title, context.Message.Id);
context.Push(caseCreated);
}
}
Notice how the create case action pushes a Case object into the rule context. Every condition and action from this point on can Find this case in the rule context.
public class Case
{
public string Id { get; set; }
public string Title { get; set; }
public bool IsOpen { get; set; }
public string[] AttachmentFilePaths { get; set; }
}
Present on this Case object are two other bits of information we are looking for, the case identifier, and title.
Hopefully spelunking around in the code a bit gives you context and confidence about where your templates will get their information from.

Template Engine
At this point we could easily create a rule action which builds an email body in code an sends an email. If we do this. What happens if someone wants to change the email’s format? We edit code. Recompile. Redeploy the extension. Yuck. That is a lot to do for such a small change. This is where a template engine comes in handy.
Carrier ships with support for creating email’s using the Spark view engine. In this case the view is an HTML email populated by anything found within the rule context.
Create Case Notification Example
Here is the create_case_success.spark template used by the Email Agent extension to populate the create case notification email.
<var caseCreated="Context.Find[[Case]]()"/>
<html>
<body>
<h1>Case ${caseCreated.Id} Created</h1>
<p>
We have opened a support case (${caseCreated.Id}) based on your email titled ${caseCreated.Title}.
</p>
<!--
<p>
Did you know you can track your case at our support web site?
<br/>
<a href="http://support.yourdomain.com/Cases/Show/${caseCreated.Id}">View Case ${caseCreated.Id}</a>
</p>
-->
</body>
</html>
The only thing provided to the template by Carrier is the rule Context from which the Case is being retrieved. Remember that the CreateCase action (above) pushed the Case object into the rule context.
Notice is how we get the Case object out of the rule context. The rule context’s Find method requires a generic argument which has a special syntax in spark. If you needed get another type of object out the the rule context just replace Case with the object’s type.
<var caseContact="Context.Find[[Contact]]()"/>
Notice how the example has the link to the fictitious self service web site commented out. Hopefully you get the idea that customizing this template would not be hard.
More .spark files are included with Carrier’s Email Agent extension for other situations like logging a note to a case or subcase.
Administrative Example
Here is a more complicated example which pulls a few more interesting bits from the rule context. This template is used, in the unfortunate event of a water landing, by the Email Agent extension to send your administrator exception details.
<var exception="Context.Find[[Exception]]()"/>
<var ruleSetName="Context.Find[[RuleSetSettings]]().RuleSetName"/>
<var message="Context.Message"/>
<html>
<body>
<h1>Email Agent Error</h1>
<p>
Email Agent encountered an error while processing the ruleset <strong>${ruleSetName}</strong>.
The original email message (id:${Context.Message.Id}) causing this error is attached to this message.
Note: the original email may not be attached if for some reason it was already deleted by the ruleset.
</p>
<h2>Email Body</h2>
${message.Body}
<h2>Exception Details</h2>
<p>The following is detailed information about the exception thrown by the ruleset.</p>
<br/>
<p>Error Message: ${exception.Message}</p>
<h3>Stack Trace</h3>
<pre>${exception.StackTrace}</pre>
<h3>Verbose Output</h3>
<pre>${exception.ToString()}</pre>
</body>
</html>
This administrative example uses the exception, which is automatically pushed into the rule context by Carrier’s error handling, and the rule set name to give the Carrier administrator exciting details about the problem. The troubled email is sent along with the error notification for potential republishing. For more details check out the source for the NotifiyAdministrator action.
Conclusion
Hopefully this post gives you confidence, and supplementarily the information, you need to customize Email Agent extension email response templates. If you are interested in creating new rule actions that use the Spark view engine to generate emails please take a look at the source code included with Carrier. In particular the SendContactACaseCreatedNotification class.
I wanted to discuss RuleSets in more detail this time diving deeper into a couple of topics I pulled out of Creating Your First Carrier Extension because they were too advanced for an introduction.
RuleSets Have Context
Your conditions and actions need a way to inspect the message they are evaluating. This is accomplished via the RuleContext object with is give to each condition and action being invoked by the RuleSet. Everytime Carrier starts executing your RuleSet it creates a new RuleSetContext object and puts the message being processed into the context.
Here is the rule context interface.
public interface IRuleContext<MESSAGE>
{
MESSAGE Message { get; set; }
T Find<T>() where T : class;
bool Has<T>() where T : class;
T Push<T>(T entity) where T : class;
}
It is also useful to have a way to communicate state between conditions and actions. This is what the other methods are for. They allow some things to Push objects into the rule context and others to Find or test to see if the context Has that state present.
Example
In Email Agent there is a condition which detects if the email is regarding a support case.
public class EmailHasCaseId : ICondition<IncomingEmailMessage>
{
private readonly IDovetailSDKToolkitAdapter _dovetailSdkToolkitAdapter;
private readonly EmailAgentSettings _emailAgentSettings;
public EmailHasCaseId(IDovetailSDKToolkitAdapter dovetailSdkToolkitAdapter, EmailAgentSettings emailAgentSettings)
{
_dovetailSdkToolkitAdapter = dovetailSdkToolkitAdapter;
_emailAgentSettings = emailAgentSettings;
}
public bool IsSatisfied(IRuleContext<IncomingEmailMessage> context)
{
var caseNoteFormat = _emailAgentSettings.CaseIdentifierFormat;
var match = Regex.Match(context.Message.Body + context.Message.Subject, caseNoteFormat);
if(!match.Success) return false;
var @case = _dovetailSdkToolkitAdapter.GetCase(match.Groups["caseId"].Value);
if(@case == null) return false;
context.Push(@case);
return true;
}
}
This condition looks for the case identifier in the email message body and subject. When a case Id is found a class describing the case is pushed into the rule context. Later on other rule actors don’t have to touch the database to look up details about the case they find details about the case in the rule context.
RuleSet Organization
Try to keep your RuleSets focused. They should really only have one reason to exist. What is this RuleSet trying to accomplish. Anytime you feel that you wish Carrier had a branching If statement it likely means you need to create a new RuleSet solving that particular problem.
Here is a common organization of rules in a RuleSet:
- First few rules should answer the question. “Should this ruleset process this Message?”
- Process the Message
- Clean Up
For example Email Agent is broken up into three RuleSets. One for creating cases, another for Logging notes to cases, and another for logging notes to subcases. Here is the RuleSet in charge of logging notes to a case.
The first two rules verify that this rule cares about this message.
The next three rules are the meat they get the job done.
The last rule cleans up.
Extensions are the heart of Dovetail Carrier. The application itself does nothing unless there is an extension present. This makes it important to understand how to create your own extensions. This guide will take you through creating a brand new extension and having it subscribe to one of the core Carrier messages IncomingEmailMessage.
Create The Extension Project
The first thing we are going to do is add a project to the extensions solution included with Carrier. Before you can do this you’ll need to unzip the \developer\extensions-source.zip file shipped with Dovetail Carrier into a directory on a development machine which has Visual Studio 2008 installed.
Next open the extension solution (carrier-extensions.sln) and add a new project to the solution.
- File -> Add -> New Project
- Select a C# Class Library and give it a name. I'll use MyFirstExtension
- Add a reference to the following Carrier assemblies found in this path relative to the solution ..\..\tools\Carrier-Core
- Dovetail.Commons
- Dovetail.Carrier.Core
- Dovetail.Carrier.Messages
- StructureMap
- Remove the Class1.cs file. We won’t be using it.
Mark the Project As A Carrier Extension
When Carrier is starting up it needs a way to determine which assemblies are extensions. The way to do this is to mark your assembly with the CarrierExtension attribute. Add the assembly attribute to the end of your AssemblyInfo.cs file.
using Dovetail.Carrier.Core;
...
[assembly: CarrierExtension]
Don’t forget to add the using statement to the top of the file.
Messaging?
Dovetail Carrier is basically super simple turnkey Enterprise Service Bus. This means that it is message oriented. Things publish messages and things subscribe to them. The behavior of the system evolves from the exchange of these messages and how the system responds to them.
Email Publisher
Carrier has an email services library which provides a message publisher which can connect to one or more POP3 email accounts. This publishing service produces messages of the type IncomingEmailMessage. Carrier is smart enough to only start this publisher up if there are subscribers present for the messages it produces.
Incoming Email Message
The messages produced by the email services publisher looks like this:
public interface IncomingEmailMessage
{
string Id { get; set; }
string Pop3UserName { get; set; }
string Pop3HostName { get; set; }
string[] To { get; set; }
string[] CC { get; set; }
string[] BCC { get; set; }
string From { get; set; }
string Subject { get; set; }
string Body { get; set; }
DateTime DateReceived { get; set; }
bool HasAttachments { get; set; }
bool HasBouncedDeliveryStatusNotification { get; set; }
}
Note: Messages should be immutable so down the road we’ll likely be removing the property setters.
To do something with this message we need some code that subscribes to these messages.
Using RuleSets To Subscribe To Messages
Carrier has a construct called RuleSets which help organize how your extensions behave when receiving messages. You organize your business behaviors into named RuleSets each having one or more rules executed one after another. Every time a message is received Carrier creates new instances of all the RuleSets defined for that type of message executes them.
Add the following file to your project named MyFirstRulesRegistry.cs.
using System;
using Dovetail.Carrier.Core.RulesEngine;
using Dovetail.Carrier.Messages;
namespace MyFirstExtension
{
public class MyFirstRulesRegistry : RulesRegistry
{
public MyFirstRulesRegistry()
{
DefineRuleSetFor<IncomingEmailMessage>()
.Named("My first rule set")
.WithRules( rule =>
{
rule.When<OnBreak>().Do<ComeBackLater>();
rule.Unless<OnBreak>().Do<Respond>();
});
}
}
}
The RulesRegistry above defines a single RuleSet consisting of two rules. Take a look at DefineRuleSetFor. This is the start of a RuleSet subscribing to IncomingEmailMessages having two rules.
Read the rules aloud to yourself and you’ll hopefully understand their behavior. We want rules to read a bit like English. Take a look at the first rule.
rule.When<OnBreak>().Do<ComeBackLater>()
When the OnBreak condition is true. The ComeBackLater action will be executed. Conditions and actions are implemented as classes in code which implement the meaning behind the language.
Intellisense
Go try editing or adding a rule in Visual Studio. You will get Intellisense assisting you with what your potential options are. It will also help you out by filling in those icky yet required angle brackets and parenthesis.
What‘s In A Rule?
Each rule defined in the RuleSet has a condition class which controls whether an action class will get executed. You can think of the rule as outlining a message processing step. The rules in your RuleSet composing a desired business behavior.
The meat of each rule is the condition and action classes implementing the behavior behind the language of the rule. Let’s implement the condition and actions in play in this RuleSet.
public class OnBreak : ICondition<IncomingEmailMessage>
{
public bool IsSatisfied(IRuleContext<IncomingEmailMessage> context)
{
var hourOfTheDay = DateTime.Now.Hour;
var areWeAtLunch = (hourOfTheDay > 11) && (hourOfTheDay < 13);
return areWeAtLunch;
}
}
public class ComeBackLater : IAction<IncomingEmailMessage>
{
public void Execute(IRuleContext<IncomingEmailMessage> context)
{
//call the customer back later.
}
}
public class Respond : IAction<IncomingEmailMessage>
{
public void Execute(IRuleContext<IncomingEmailMessage> context)
{
//respond to the email.
}
}
Take a look at the OnBreak class it has an IsSatisfied method which will get executed during rule evaluation. The rule’s action will get executed When this condition returns true. OnBreak condition will return true when the current time of day is around noon.
Conditions are technically optional as you can define an AlwaysDo action by itself.
Each rule can also end with an Exit() command which will also stop execution of the ruleset when the condition is satisfied.
These actions and conditions don’t really do much take a look at my other Carrier posts for more complex actions and conditions.
Testing Your Extension
Well the best way to make sure everything is working well is to test. Here at Dovetail we are really into automated testing. We like to write unit tests using NUnit and automate their execution. Included with your extension source are many examples of testing conditions and actions.
We have included the test suites used in developing all of our shipping extensions. You can run these tests from a command line by running the build-extensions.bat batch file found in your extensions source directory. If you want to learn more about the automation behind build-extensions.bat please read up on the excellent build automation tool NAnt.
Deploying Your Extension
It should be clear to you that this extension does not really do anything useful. It is more of an outline bootstrap your understanding of Carrier’s extensibility. For a more complete example demonstrating producing as well as consuming messages please check out my Clock – A Super Simple Carrier Extension post.
If you wish to see this extension in action.
- I recommend removing any of the other extensions you may have installed. Carrier.Extensions.*.dll.
- Next build your project and copy the resulting .Net assembly, MyFirstExtension.dll, into the Carrier service application directory.
- Make sure your pop3-accounts.config is configured with a valid email account. I highly recommend hMailServer when testing email sending and receiving.
- Run carrier from a command line. DovetailCarrierService.exe
- You’ll likely not see much as the conditions and actions we created above have no logging going on and don’t really do anything.
Conclusion
I’ve taken you through a start to finish creation of an essentially do nothing extension. My goal was to introduce you to the basic developer facing concepts you’ll run into creating and customizing Dovetail Carrier. Please do let me know what you want to know more about. I’ll be happy to help.
At Dovetail we use StructureMap a lot. Lately we’ve also been using Topshelf a lot too. This post takes a look at how you can marry these two technologies together. We will be using the StrucutreMap container to configure which services get started by Topshelf at runtime.
Simple Services
Let’s start simple. We need a way to identify which configured types want to get configured to be spun up by Topshelf. To accomplish this I created an interface called ISimpleService which includes only the Start and Stop methods that Topshelf uses to manage the services lifetime.
public interface ISimpleService
{
void Start();
void Stop();
}
Next up we’ll update and expand the example from my previous post Controlling Application Lifetime In Topshelf to have it implement the ISimpleService. We’ll also add another service in charge of spamming the log file.
public class LogSpamService : ISimpleService
{
private readonly ILogger _logger;
private Timer _timer;
public LogSpamService(ILogger logger)
{
_logger = logger;
}
public void Start()
{
_timer = new Timer(SpamTheLog, this, TimeSpan.Zero, TimeSpan.FromSeconds(1));
}
private void SpamTheLog(object state)
{
_logger.LogInfo("spam spam spam");
}
public void Stop()
{
_timer.Dispose();
}
}
Configuring Simple Services
Here comes the magic. I created a handy Topshelf configuration extension method for registering all the types in the container registered implementing the ISimpleService interface.
public static class TopShelfRunnerConfiguratorExtensions
{
public static void ConfigureSimpleServices(this IRunnerConfigurator configurator, IContainer container, ILogger logger)
{
var simpleServiceInstances = container.Model.InstancesOf<ISimpleService>();
foreach (var simpleServiceInstance in simpleServiceInstances)
{
var requiredServiceType = simpleServiceInstance.ConcreteType;
logger.LogDebug("Configuring runtime to manage the lifecycle of the service {0}.", requiredServiceType);
configurator.ConfigureService<ISimpleService>(service =>
{
service.HowToBuildService(name => container.GetInstance(requiredServiceType));
service.WhenStarted(s => s.Start());
service.WhenStopped(s => s.Stop());
});
}
}
}
This extension method will be used to configure Topshelf to start up all of the ISimpleService types present. Next all that is left to do is update the configuration code to use our new extension method.
Starting Up The Application
internal class ProgramExample
{
private static void Main(string[] args)
{
Environment.CurrentDirectory = AppDomain.CurrentDomain.BaseDirectory;
var container = new Container(cfg =>
{
cfg.AddRegistry<CommonsRegistry>();
cfg.Scan(scan=>
{
scan.TheCallingAssembly();
scan.AddAllTypesOf<ISimpleService>();
});
});
var logger = container.GetInstance<ILogger>();
var runConfiguration = RunnerConfigurator.New(config =>
{
config.ConfigureSimpleServices(container, logger);
});
container.Inject(runConfiguration.Coordinator);
Runner.Host(runConfiguration, args);
}
}
First I tell StructureMap to scan the assembly and register all the ISimpleServices it finds. Then we use the Topshelf extension method to configure all the simple services. In this case we have two services present LogSpamService and the HealthMonitoringService.
Let It Rip
The application output shows the two services being started. A lot of spam being logged and eventually the health monitor shuts the application down.
Conclusion
The real power behind using an IoC container to drive the configuration of the Topshelf application host is that we can now add a service to be started at runtime by simply implementing a single interface.
A more advanced scenario in use at Dovetail is to only start up the services which are necessary. We have an application with a plug-in architecture where there may be services present in the application that are not necessary because the plug-ins that consume the output of the service are not in use.
One of Topshelf’s best features is the ability to easily host one or more services running side by side. In this post we will create a Topshelf service which will shut down the application. Lets take a look at how it is done.
Coordinating the Service
Let’s create a fake Health Monitoring service which will, sometime in the future, tell Topshelf to shut the application down. The Topshelf hook for controlling the state of all the services it is hosting is a type called IServiceCoordinator. Here is the code for our health monitor.
public class HealthMonitoringService : IHealthMonitoringService
{
private readonly IServiceCoordinator _serviceCoordinator;
private readonly ILogger _logger;
private Timer _timer;
private int _healthCheckCount = 0;
public HealthMonitoringService(IServiceCoordinator serviceCoordinator, ILogger logger)
{
_serviceCoordinator = serviceCoordinator;
_logger = logger;
}
public void Start()
{
_logger.LogWarn("The Health Monitor is starting up");
_timer = new Timer(HealthCheck, this, TimeSpan.Zero, TimeSpan.FromSeconds(5));
}
private void HealthCheck(object state)
{
//determine if the service should stop.
var shouldStopService = (_healthCheckCount++ > 2);
_logger.LogDebug("We are {0}going to stop the service.", (shouldStopService) ? "" : "not ");
if(shouldStopService)
{
_logger.LogWarn("The Health Monitor is stopping the windows service.");
_serviceCoordinator.Stop();
}
}
public void Stop()
{
_healthCheckCount = 0;
_timer.Dispose();
}
}
This service uses a timer to do a “healthcheck” every 5 seconds. The health check will tell the service coordinator to stop after 3 health checks. We also do a bunch of logging which will give us something to look at while the application is running.
Configuring Topshelf
Next up we need to register our service with Topshelf telling it how create and start up our health monitor.
internal class ProgramExample
{
private static void Main(string[] args)
{
Environment.CurrentDirectory = AppDomain.CurrentDomain.BaseDirectory;
var container = new Container(cfg =>
{
cfg.AddRegistry<CommonsRegistry>();
cfg.For<IHealthMonitoringService>().Use<HealthMonitoringService>();
});
var runConfiguration = RunnerConfigurator.New(config =>
{
config.ConfigureService<IHealthMonitoringService>(service =>
{
service.Named("Health Monitor");
service.HowToBuildService(builder => container.GetInstance<IHealthMonitoringService>());
service.WhenStarted(s => s.Start());
service.WhenStopped(s => s.Stop());
});
});
container.Inject(runConfiguration.Coordinator);
Runner.Host(runConfiguration, args);
}
}
Note this example uses StructureMap to push the IServiceCoordinator and ILogger dependencies into the instance of the health monitor created by Topshelf. This requires that once we have configured Topshelf we have to inject the IServiceCoordinator into the container otherwise it will not be found and StrucutreMap when creating the IHealthMonitoringService and an exception will be thrown.
You might be wondering where the ILogger comes from. I have a thin wrapper around Log4Net which I use in many applications so I have created a commons library. I’ve talked about it in more detail up on StackOverflow. The CommonsRegistry is in charge of setting up how ILoggers get created.
Show Me the Health Check
You can just as easily run this console application as a windows service and this same code will automatically stop the windows service. Obviously this application is not too useful on its own. It works better when you configure more than one service to be running at the same time. But that sounds like another blog post.
Let’s set aside TwitterAgent for a bit and create the simplest extension to Dovetail Carrier possible, a timepiece, a clock, a chronometer of sorts. The intent of this post is to document in one place all the pieces required to create a Carrier Extension.

The Marker
For now we need to mark the assembly with an attribute announcing it as a Carrier extension.
[assembly: CarrierExtension]
You just need to put this code somewhere but it usually goes in your AssemblyInfo.cs file.
The Message
We first need to create the message we’ll be publishing and consuming.
public class ClockMessage
{
public string Announcement { get; set; }
}
The Publisher
Next up we need to create a service which will publish these messages.
public class ClockMessageService : IMessagePublishingService<ClockMessage>
{
private readonly MessageBusSettings _messageBusSettings;
private readonly IEndpointFactory _endpointFactory;
private readonly ILogger _logger;
private Timer _timer;
public ClockMessageService(MessageBusSettings messageBusSettings, IEndpointFactory endpointFactory, ILogger logger)
{
_messageBusSettings = messageBusSettings;
_endpointFactory = endpointFactory;
_logger = logger;
}
private void onTimer(object state)
{
var endpoint = _endpointFactory.GetEndpoint(_messageBusSettings.MessageQueue);
var announcement = String.Format("At the tone the time will be {0}.", DateTime.Now.ToLongTimeString());
endpoint.Send(new ClockMessage { Announcement = announcement });
}
public void Start()
{
_logger.LogInfo("Starting test message service");
_timer = new Timer(onTimer, null, TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(10));
}
public void Stop()
{
_logger.LogInfo("Stopping test message service");
_timer.Dispose();
}
}
This service uses a timer to publish a ClockMessage every 10 seconds. Each message’s announcement is an homage to a bygone era when we didn’t have time pieces embedded into everything.
Defining RuleSets
public class ClockRuleRegistry : RulesRegistry
{
public ClockRuleRegistry()
{
DefineRuleSetFor<ClockMessage>("Just log the message", rule =>
{
rule.AlwaysDo<LogAction>();
});
For<IMessagePublishingService<ClockMessage>>().Use<ClockMessageService>();
}
}
A RuleRegistry is used to define one or more RuleSets. In this case the lone RuleSet will always write the Announcement to the logger. The registry will additionally configure the message publishing service.
This is typically where you add functionality related to your business domain. What exactly do you want to do with a Clock Message in your business? :)
The Action
I needed the RuleSet to do something. So I made the simplest action I could think of. Log It!
public class LogAction: IAction<ClockMessage>
{
private readonly ILogger _logger;
public LogAction(ILogger logger)
{
_logger = logger;
}
public void Execute(IRuleContext<ClockMessage> context)
{
_logger.LogInfo(context.Message.Announcement);
}
}
The Deployment
All you need to do for this extension to work is to drop it into the Carrier application directory.
To test I run Dovetail Carrier from the command line
Highlights are mine. You can see the ClockMessageService starting up. Messages are being received in the message queue and then being handled immediately by the “Just log the message” RuleSet.
All Done
I hope this one stop, soup to nuts, customization post for Dovetail Carrier helps you out.
The next step in our plan to implement a Twitter version of Email Agent using Dovetail Carrier is to consume the direct messages coming from Twitter which we published last time. Message consumers basically…er, well they consume messages. The beauty of the message bus is that we have the ability to subscribe many consumers to the types of messages which we publish. Dovetail Carrier provides a way to compose your message consumers by assembling a set of conditions and actions called RuleSets.
This RuleSet we’ll be examining will investigate an incoming tweet, find the case id (731), and log a note the case.
A Note before I continue: we will be including the Twitter Agent code with future releases of Dovetail Carrier. Additionally, with each release of Carrier you get a lot more extension code examples as we include the source code for our baseline Dovetail Carrier extensions (currently Email Agent, and Communications.)
Introducing the Carrier RuleSet
We wanted to provide a framework which breaks up the processing of the message into a set of conditions and actions called rules. Each rule in the set is executed in order. Rules can have conditions that act as a gate for an action that can be invoked. Additionally, each rule in the set can elect to quit execution of the whole RuleSet and prevent more rules from being executed.
To simplify RuleSet definition we have created a domain specific language (DSL) for defining rules. Let’s take a look at the definition for a very simple RuleSet used by Twitter Agent to log Tweets to cases.
My First Rule Set
DefineRuleSetFor<IncomingDirectMessage>("A direct message can log a note to a case.", rule =>
{
rule.Unless<CaseIdIsPresent>().Exit();
rule.AlwaysDo<LogNoteToCase>();
});
This rule set basically looks for a case identifier in the direct message and when found logs the direct message to the case.
DefineRuleSetFor<IncomingDirectMessage>
If you are not familiar with C# generics the angle brackets surrounding a Type represent the type of .Net object being worked with. You can read this as “Define a rule set for the message type IncomingDirectMessage.” You may remember that this direct message type is the same one we learned how to publish last time.
"A direct message can log a note to a case.",
This is a little description of the rule set which is helpful for summarizing the intent of the RuleSet. These are also used in the application log for context about the message being processed.
rule => { }
This is a lambda expression giving you a rule expression object with which you’ll define rules using a, hopefully easy to read, fluent interface. If you are a little confused and scared about lambdas. It’s just a block of code that gets executed later. Instead of worrying let’s look at more interesting things like next line of code.
rule.Unless<CaseIdIsPresent>().Exit();
The first rule in each rule set is usually a gate that checks to see if the RuleSet should continue. In this case we only want to log a note to a case if we find a reference in the Tweet to a case id. CaseIdIsPresent is a class implementing a condition interface you will need to implement.
Thankfully the condition interface is very simple.
public interface ICondition<MESSAGE>
{
bool IsSatisfied(IRuleContext<MESSAGE> context);
}
We will take a look at the code for the CaseIdIsPresent condition in a little bit. First I want to introduce you to actions and the final rule in the set.
rule.AlwaysDo<LogNoteToCase>();
Once we’ve determined that we’ll be processing this direct message we log a note to the case. LogNoteToCase is a class implementing an action interface which, you have likely guessed, need to implement. The Action interface is also very simple.
public interface IAction<MESSAGE>
{
void Execute(IRuleContext<MESSAGE> context);
}
Again we will look at the LogNoteToCase code in a bit. I promise. Really. First though I have one more concept I need to introduce.
When implementing the Execute method of the action we’ll need to know the case id so that we can log the Tweet to it. You likely noticed that the CaseIdIsPresent condition was in charge of detection if a legitimate case id is present. Wouldn’t it be nice to be able to communicate important information between conditions and actions and across rules? That is a nice segue into Rule Contexts.
Rule Context
Each condition and action will need some information to get their job done. Most importantly they will need the message being processed. Before each RuleSet is processed Carrier creates a brand new rule context giving it the message to be processed. Each condition and action receives the Rule Context.
It will quickly become apparent that keeping some state around between different conditions and actions is necessary. In the example above the first rule looks for a valid case id in the direct message. If it finds an id it would be handy to push that id into the rule context so that future conditions and actions can know what the case id is without having to figure it out on their own.
Passing information into and between condition and action executions is the job of the rule context.
public interface IRuleContext<MESSAGE> : IRuleContext
{
MESSAGE Message { get; set; }
}
public interface IRuleContext
{
T Find<T>() where T : class;
bool Has<T>() where T : class;
T Push<T>(T entity) where T : class;
}
You likely noticed that each action and condition gets one of these passed to them. It will likely help to see the rule context in action so let’s take a look at the CaseIdIsPresent condition code.
My First Condition
A condition minimally needs to return true or false. Along the way you might find it useful to Push or Find objects into and out of the rule context.
public static class TweetExpressions
{
public static Regex CaseIdExpression = new Regex(@"case\s(?<caseid>\w+)|#(?<caseid>\w+)", RegexOptions.IgnoreCase);
}
public class CaseIdIsPresent : ICondition<IncomingDirectMessage>
{
private readonly IDovetailSDKToolkitAdapter _dovetailSdkToolkitAdapter;
public CaseIdIsPresent(IDovetailSDKToolkitAdapter dovetailSdkToolkitAdapter)
{
_dovetailSdkToolkitAdapter = dovetailSdkToolkitAdapter;
}
public bool IsSatisfied(IRuleContext<IncomingDirectMessage> context)
{
var match = TweetExpressions.CaseIdExpression.Match(context.Message.Text);
if(!match.Success)
return false;
var caseId = match.Groups["caseid"].Value;
var @case = _dovetailSdkToolkitAdapter.GetCase(caseId);
if(@case == null)
return false;
context.Push(@case);
return true;
}
}
This condition uses a regular expression to see if an case id is present in the Tweet. When one is found we check to see if the case exists and if so we push the case into the rule context for future conditions and actions to have access to.
The IDovetailSDKToolkitAdapter class is helper code included with Carrier which wraps our Dovetail SDK and creates a helpful little Case object with only the current useful bits.
public class Case
{
public string Id { get; set; }
public string Title { get; set; }
public bool IsOpen { get; set; }
}
Nothing special there and it is easy to write your own equivalent, in fact we’ll do that for the Log Note action. I like to keep data access code sequestered to a separate class keeping the concern of the condition focus on its sole purpose of finding the case id and pushing it into the rule context.
My First Action
Actions do things. That is all. Along the way they too may use and add to the rule context to make decisions about what to do.
public class LogNoteToCase : IAction<IncomingDirectMessage>
{
private readonly ITwitterExtensionDovetailSDKToolkitAdapter _dovetailSdkToolkitAdapter;
public LogNoteToCase(ITwitterExtensionDovetailSDKToolkitAdapter dovetailSdkToolkitAdapter)
{
_dovetailSdkToolkitAdapter = dovetailSdkToolkitAdapter;
}
public void Execute(IRuleContext<IncomingDirectMessage> context)
{
var @case = context.Find<Case>();
_dovetailSdkToolkitAdapter.LogNoteToCase(@case.Id, context.Message.Text);
}
}
This action gets the Case object form the rule context and uses it to log a note to the case. El Fin.
In this case I have created my own Dovetail SDK adapter because the one included with Carrier did not do log note to case.
More About RuleSets
We already showed one example of defining a simple RuleSet. Let’s dive in a bit deeper and see what else there is that we haven’t talked about.
Rule Definitions
In the example above we missed a few of the other rule definit ion options. Here is a sample of everything you can currently do.
DefineRuleSetFor<MESSAGETYPE>("Ruleset name or description.", rule => {
rule.When<ConditionIsTrue>.Do<Action>();
rule.Unless<ConditionIsFalse>.Do<Action>();
rule.When<WeShouldExitTheRuleSet>.Exit();
rule.AlwaysDo<Action>();
});
When<Condition> will execute an action when the condition is true.
Inversely a Unless<Condition> will execute the action when the condition is false.
When your rule does not require a condition you can use AlwaysDo<Action> to define an action that will always run.
You can always tack an Exit() onto a rule which will stop execution of the RuleSet when the condition is true (if present.)
We do not support branching behavior of RuleSets. The idea is to keep your RuleSets small, simple, and targeted at doing one thing. If you find yourself wanting a branch within your RuleSet it likely means you should be creating another RuleSet.
The first few rules in the set will likely be gates that decide if the RuleSet that should handle the message being processed.
Database Transactions
The invocation of each RuleSet is done within the context of a database transaction. Your data access classes are highly encouraged to participate in this transaction. Thankfully we make it easy. Let’s take a look a the data access code for LogNoteToCase.
public interface ITwitterExtensionDovetailSDKToolkitAdapter
{
void LogNoteToCase(string caseId, string notes);
}
public class TwitterExtensionDovetailSDKToolkitAdapter : ITwitterExtensionDovetailSDKToolkitAdapter
{
private readonly IClarifyApplicationAdapter _clarifyApplicationAdapter;
private readonly IDbTransactionBoundary _dbTransactionBoundary;
private readonly DovetailCRMSettings _dovetailCrmSettings;
public TwitterExtensionDovetailSDKToolkitAdapter(IClarifyApplicationAdapter clarifyApplicationAdapter, IDbTransactionBoundary dbTransactionBoundary, DovetailCRMSettings dovetailCrmSettings)
{
_clarifyApplicationAdapter = clarifyApplicationAdapter;
_dbTransactionBoundary = dbTransactionBoundary;
_dovetailCrmSettings = dovetailCrmSettings;
}
public void LogNoteToCase(string caseId, string notes)
{
var logCaseNoteSetup = new LogCaseNoteSetup(caseId)
{
Notes = notes,
UserName = _dovetailCrmSettings.EmployeeUserName
};
var supportToolkit = new SupportToolkit(_clarifyApplicationAdapter.GetApplicationSession());
supportToolkit.LogCaseNote(logCaseNoteSetup, _dbTransactionBoundary.Transaction);
}
}
Notice the IDbTransactionBoundary interface being used by this data access class. Ignore for now how it gets pushed into the constructor. We will talk about that in a little bit. The Transaction property on this class is guaranteed to be automatically committed when the RuleSet is complete or rolled back if something throws an exception. All you need to remember to do is give it to all of your code that is doing data access. In this case we are delegating to the Dovetail SDK how notes are logged.
This is a powerful yet simple to use feature of Carrier RuleSets that frees you from the drudgery of transaction management. Now what is with all these interfaces being used by the constructors of all these classes.
Inversion Of Control (IoC)
You’ve likely been looking at the constructors in the actions, conditions and the data access class examples above and been wondering why I am pushing in dependencies of the class and not “newing” them up within the class. Dovetail Carrier is built from the ground up around a technique called the Dependency Inversion (DI) principal. This is a big enough topic for a few more blog posts but I will try to summarize.
When building classes we try to push our dependencies into the class (typically as interfaces.) This keeps our code flexible and loosely coupled which means that you can easily change the implementation of dependencies. More importantly you can easily test class behavior by swapping out dependencies for test fakes.
Wait, What Was That?
Did I lose you? Don’t worry it’s less complicated than it sounds. Basically because we are employing something called a Container the construction of any dependencies present in your class’s constructor are auto wired or created and injected for you into the class.
Behind the scenes when your RuleSet is getting ready to be executed we use a container to create your RuleSet which includes all of its conditions and actions and their dependencies. This is why we can easily separate our actions from data access keeping them loosely coupled and easy to unit test.
Remember Transactions? When your conditions and actions take a dependency on IDbTransactionBoundary the container will give your class access to the RuleSet’s active transaction.
What Is Next?
Hmm sorry if that last bit about Dependency Injection lost you. I know it may be a new concept but there is big power there. Hopefully these introductory posts have given you a sense of what it takes to create a Carrier extension.
Here are some ideas I have for follow up posts on Dovetail Carrier.
- RuleSet testing. Show off how easy it is to test your rule sets.
- A post about RuleSet registration would surely be helpful.
- Another post on Twitter Agent featuring the RuleSet we skipped over. Creating cases based on Tweets.
Previously we made a plan to go get Twitter direct messages and publish them into the Dovetail Carrier message bus. Thankfully the Twitter API is pretty simple but there are a few things to worry about.
- We need to listen to Twitter for direct messages.
- Transforming and publishing the direct message into the message bus.
- Host your Twitter polling and publishing service with Carrier.
First Things First
This is not a cookbook post. I don’t describe each step along the way that I took to create a Carrier Twitter extension. That said I hope to provide the Visual Studio solution and code for this example in a future version of Carrier.
A general house keeping item. To start I created a new project solution called Carrier.Extension.Twitter and added references to Carrier.Core and Dovetail.Commons.
Carrier Extension Attribute
For now we need a way to tell Carrier that your assembly should be treated as an extension assembly. For now the way to do this is to add an attribute to the AssemblyInfo.cs file of your extension.
[assembly: CarrierExtension]
Extension Deployment
Right now to deploy your extension you’ll need to drop your extension assembly and it’s dependencies into the Carrier application directory. We hope to improve on how Carrier locates an configures extensions down the road.
Using Tweet Sharp
The first real code we need is something that will go get new direct messages from Twitter. I really don’t want to tackle and maintain the code that is responsible for the nitty gritty details required to talk directly to Twitter. I looked around and found quite a few C# libraries and after asking around on Twitter I settled on TweetSharp.
TweetSharp works well. They have all access to Twitter wrapped in a Fluent DSL which I found a little tricky to get used to but using examples I was able to make it work for my simple use case. I created a class called TwitterStream to encapsulate all my access to Twitter. Here is the meat of how I am using TweetSharp:
public IEnumerable<TwitterDirectMessage> GetDirectMessages()
{
// Get the public timeline
var twitter = AuthenticatedRequest().DirectMessages().Received();
AddSinceClause(twitter);
var response = twitter.Request();
var directMessages = response.AsDirectMessages();
RememberMostRecentDirectMessageIdentifier(directMessages);
CheckResponseForError(response);
return directMessages;
}
Avoid getting duplicate Tweets
Twitter’s API lets you optionally specify the last Tweet ID that you’ve received to avoid getting duplicate Tweets back in your request. I added support for this to TwitterStream via a little repository which is in charge of remembering the id of the last direct message we’ve received. I punted and created a simple in-memory version of the repository but it would be easy to add a bit of schema and create an twitter identifier repository which saves to the database.
private void AddSinceClause(ITwitterDirectMessagesReceived twitter)
{
var mostRecentTweetId = _identifierRepository.Get();
if (mostRecentTweetId.HasValue)
twitter.Since(mostRecentTweetId.Value);
}
private void RememberMostRecentDirectMessageIdentifier(IEnumerable<TwitterDirectMessage> directMessages)
{
if (directMessages != null && directMessages.Count() > 0)
_identifierRepository.Save(directMessages.First().Id);
}
Here is the gist of the full version of TwitterStream. Next we need to add some code that will poll Twitter for new direct messages.
Listening For Tweets
Twitter limits your API calls to 150 per hour. Which means that you can check every 30 seconds for new direct messages with out risk of exceeding your limit. It would be easy to create a Timer that fires every 30 seconds which might work fine but what if you are checking more often or what if Twitter takes a long time to get back to you? It is possible that your timer events can start to overlap and execute concurrently. This could cause you to get the same message twice.
I’ve had to write code to compensate for this a few times so I pulled the common code into an abstract class Listener<T> where the T is the type of the message being received. We will be moving this code somewhere Common. All that is left to do is derive a class from Listener<T> and implement the Poll method firing the OnMessageReceived event each time a direct message is received.
public class TwitterDirectMessageListener : Listener<TwitterDirectMessage>
{
private readonly ITwitterStream _twitterStream;
public TwitterDirectMessageListener(ITwitterStream twitterStream, ILogger logger) : base(logger)
{
_twitterStream = twitterStream;
}
public override void Poll()
{
_logger.LogDebug("Getting direct messages.");
var directMessages = _twitterStream.GetDirectMessages();
foreach (var directMessage in directMessages)
{
OnMessageReceived(directMessage);
}
_logger.LogDebug("Done getting direct messages.");
}
}
Once we get the direct message it will need to be published to the message bus.
Publishing Direct Messages to the Bus
The final leg of our journey involves transforming the direct message that we get from TweetSharp, which is a class called TwitterDirectMessage (the source). We could publish this as a message to the bus, but this is not a good idea. It is recommended to use a dedicated message type. We will be creating a message type called IncomingDirectMessage.
What Could Go Wrong If you Don’t Use a Dedicated Message Type?
The short answer is that any changes to the message type could break compatibility with anything in your message bus which is subscribed to that message.
Say we used TweetSharp’s TwitterDirectMessage in our service bus we would run into problems if the next version of TweetSharp changes the TwitterDirectMessage’s type signature. The service bus would only know how to de/serialize one version of the message. If an older message was laying around serialized against an old message format. The message would be unusable. The enterprise service bus would do it’s best but chances are it would fail.
Control Your Message Types
You want to have very good control over the message types used by your message bus to avoid compatibility problems between different versions of the components that consume messages. The general guidance handed down from those with more experience than myself is that anytime you change a message in use in production is that you should create a new message type.
One important thing to know is that strongly naming the assembly that contains your message type will cause the signature of the message to change whenever the version number of the assembly changes. So it is likely a good idea to keep your messages in a separate assembly whose version number does not change.
Assembling An IncomingDirectMessage
To control contents of the message which we are publishing to our message bus I created a type called IncomingDirectMessage:
public class IncomingDirectMessage
{
public long Id { get; set; }
public DateTime CreatedDate { get; set; }
public long SenderUserId { get; set; }
public string SenderScreenName { get; set; }
public string SenderName { get; set; }
public string SenderProfileImageUrl { get; set; }
public string Text { get; set; }
public long RecipientUserId { get; set; }
public string RecipientScreenName { get; set; }
public string RecipientName { get; set; }
public string RecipientProfileImageUrl { get; set; }
}
To transform TweetSharp’s TwitterDirectMessage into one of our IncomingDirectMessages I wrote a simple method whose only responsibility is this transformation.
public static IncomingDirectMessage AssembleIncomingDirectMessage(TwitterDirectMessage directMessage)
{
return new IncomingDirectMessage
{
Id = directMessage.Id,
CreatedDate = directMessage.CreatedDate,
SenderUserId = directMessage.SenderId,
SenderScreenName = directMessage.SenderScreenName,
SenderName = directMessage.Sender.Name,
SenderProfileImageUrl = directMessage.Sender.ProfileBackgroundImageUrl,
Text = directMessage.Text,
RecipientScreenName = directMessage.RecipientScreenName,
RecipientUserId = directMessage.RecipientId,
RecipientProfileImageUrl = directMessage.Recipient.ProfileBackgroundImageUrl,
RecipientName = directMessage.Recipient.Name
};
}
Note: You could also use a tool like AutoMapper to do this.
Publishing The IncomingDirectMessage
Carrier uses a library called MassTransit do all the message bus heavy lifting so publishing a message to the bus is pretty simple.
public class DirectMessagePublisher : IPublisher<TwitterDirectMessage>
{
private readonly MessageBusSettings _messageBusSettings;
private readonly IEndpointFactory _endpointFactory;
private readonly ILogger _logger;
public DirectMessagePublisher(MessageBusSettings messageBusSettings, IEndpointFactory endpointFactory, ILogger logger)
{
_messageBusSettings = messageBusSettings;
_endpointFactory = endpointFactory;
_logger = logger;
}
public void Publish(TwitterDirectMessage directMessage)
{
using (_logger.Push("DM {0}".ToFormat(directMessage.Id)))
{
var incomingDirectMessage = AssembleIncomingDirectMessage(directMessage);
_logger.LogDebug("Publishing direct message from @{0} to @{1}", incomingDirectMessage.SenderScreenName, incomingDirectMessage.RecipientScreenName);
var queueName = _messageBusSettings.MessageQueue;
var messageEndpoint = _endpointFactory.GetEndpoint(queueName);
messageEndpoint.Send(incomingDirectMessage);
_logger.LogDebug("Twitter direct message published.");
}
}
//...
}
We get the name of the queue the message bus is using from configuration via the MessageBusSettings object. The EndpointFactory is in-charge of serializing the message and sending it to the message queue being used to manage the message bus.
Putting It All Together
We’ve assembled all the pieces we need to poll, produce, and publish direct messages to the message bus. All we have left to do is compose everything together into a TwitterService which gets started up with Carrier.
The Twitter Service will do the following:
- Use the Listener to fire events when direct messages are received.
- The direct message event handler will publish the direct message as an IncomingDirectMessage to the message bus.
public class TwitterService : IMessagePublishingService<IncomingDirectMessage>
{
private readonly IListener<TwitterDirectMessage> _twitterDirectMessageListener;
private readonly IPublisher<TwitterDirectMessage> _publisher;
private readonly ILogger _logger;
public TwitterService(IListener<TwitterDirectMessage> twitterDirectMessageListener, IPublisher<TwitterDirectMessage> publisher, ILogger logger)
{
_twitterDirectMessageListener = twitterDirectMessageListener;
_publisher = publisher;
_logger = logger;
}
public void Start()
{
_logger.LogDebug("Starting Twitter Service");
_twitterDirectMessageListener.MessageReceived += TwitterListener_MessageReceived;
_twitterDirectMessageListener.Start();
}
private void TwitterListener_MessageReceived(object sender, MessageArg<TwitterDirectMessage> messageArg)
{
_logger.LogDebug("Publishing message from {0}.", messageArg.Message.SenderScreenName);
_publisher.Publish(messageArg.Message);
}
public void Stop()
{
_logger.LogDebug("Stopping Twitter Service");
_twitterDirectMessageListener.Stop();
}
}
Notice that TwitterService implements IMessagePublishingService<IncomingDirectMessage> which is essentially a marker interface declaring to Carrier that this service publishes Twitter direct messages.
Hosting Twitter Service
Now that we’ve created the TwitterService. Who is in charge of hosting it? You could write a windows service for each message producer you create but that would not scale well as you create more and more messages needing publishing. We wanted to make publishing messages easy so we let you leverage a windows service we know you’ll already have: Dovetail Carrier.
To host your service Carrier needs to a way of knowing which types of messages your service publishes. This is accomplished by implementing the IMessagePublishingService interface.
public interface IMessagePublishingService<MESSAGE> : ISimpleService { }
public interface ISimpleService
{
void Start();
void Stop();
}
During start up Carrier will compile a list of the message types used by all of the message consumers present in the application. Next, it will then go through that list to see if there is a IMessagePublishingService of each message type present. When it finds one it will configure the runtime to start up that service running on its own thread within the process of the Dovetail Carrier windows service.
You can see that the TwitterService example above is a message publishing service for IncomingDirectMessages because it implements the IMessagePublishingService<IncomingDirectMessage> interface.
The Start and Stop methods present on the interface are required to allow the runtime control over the lifecycle of the service.
Note: We will talk more about creating message consumers in the next post.
Publishing Accomplished
As you can see. Not a lot of code went into creating the Twitter Service. We leveraged the handy TweetSharp library and the Listener<T> class for polling Twitter. It also doesn’t take too much code to transform direct messages and to publish them to the message bus. And we learned how Dovetail Carrier will take care of hosting the Twitter Service when we get done creating consumers for the messages we’re publishing.
I didn’t show you any pretty screen shots... Ok, I’ll show you one but it won’t be pretty.
Now that we can push messages into the message bus what are we going to do with them? Next time we’ll talk about creating message consumers.
Dovetail Carrier facilitates the creation of an enterprise message bus where messages can be produced and more importantly consumed. We have included a framework for creating sets of rules that act as consumers of messages coming into the system.
I will be doing a series of posts around creating custom extensions for Carrier. The goal of these posts is to educate you and to force me to shine a light on the Carrier customization story in order find the rough edges begging to be smoothed out. Here is what we are going to cover.
Whatever Shall We Do?
To make all this a little more bearable. Let’s set the stage by creating a goal to shoot for while learning how to customize Carrier. This will all be more fun if we actually make something. The most straight forward thing I can think to create is a Twitter version of our EmailAgent product. Let’s get “creative” and call it TwitterAgent.
The idea is to pull direct messages from Twitter and use them to create cases or log notes to existing cases as Tweets come in. To create a Twitter version of our EmailAgent application 2 RuleSets will be put into play:
- Tweets that are direct messages to an account will create cases.
- Tweets that include case identifiers will log notes to the case.
Lets see the create case rule set in action.
Carrier is going to pick up this Tweet and publish it to Carrier’s message bus. A message consumer will pick up the Tweet and use it to create a case.

Next up we will create a Twitter listening service that pulls direct messages off Twitter and publishes them into our service bus.