Hurricane MTA Server’s event-driven plugin API has long been one of its greatest features and strengths. It enables real-time event-driven integration and data push capabilities never before seen in an MTA. Interfaces like IOutboundSMTPConncetion and its OnSent(), OnFail() and OnDefer() event methods allow developers to handle real-time events with .Net code just as easy as handling an OnClick() or Page_Load() event in other .Net apps.
These “pre-casted” event methods are easy to use, but unfortunately they are pretty much immutable. Due to the limitations imposed by the architecture of .Net we can not extend these methods or interfaces without breaking the original interface and forfeiting its backwards compatibility – and the backwards compatibility of the Hurricane MTA Server plugins you have developed.
Our answer to this is the new IDynamic plugin interface. Unlike the other interfaces supported by the Hurricane MTA Server plugin API, the IDynamic interface contains only one method, OnDynamicEvent().
object OnDynamicEvent(DynamicEventId eventId, ref Dictionary eventParams)
OnDynamicEvent() and its parameters are generic. Hurricane MTA Server can trigger this method for any type of event, which it identifies by the eventId parameter and can pass an unlimited number of parameters via the eventParams dictionary object.
Additionally since eventParams is passed by reference, plugins can add to or modify the values it contains as a way of instructing Hurricane MTA Server to do something differently. The return value of OnDynamicEvent is an object type which further enables plugins to return rich information to Hurricane MTA Server where applicable.
IDynamic is much less rigid than the other Hurricane MTA Server API interfaces and the benefit becomes clear when you realize that now we can add infinite levels of functionality to the API without breaking the interface – or your existing code!
Let’s take a look at a simple implementation of the IDynamic interface.
public class Feedbackloop : PluginBase, IDynamic { public object OnDynamicEvent(DynamicEventId eventId, ref Dictionary eventParams) { if (eventId == DynamicEventId.Init) { eventParams["OnFirstConnection"] = 1; } if (eventId == DynamicEventId.OnFirstConnection) { //do work here to handle OnFirstConnection event } return null; } }
The first thing you will notice is that since the OnDynamicEvent() method can be called for any number of events, we are checking the eventId parameter to see which event is actually being fired.
One event that we must handle in every OnDymanicEvent() implementation is Init. OnDynamicEvent() will be called with the eventId parameter set to DynamicEventId.Init the first time OnDynamicEvent() is called after the plugin is loaded. The Init event must be handled to instruct Hurricane MTA Server to call OnDynamicEvent() for other events that you want to handle. This is for performance as it would not be efficient for Hurricane MTA Server to call OnDynamicEvent() for every event it supports, even if you are not building a handler for it.
When the Init event is fired, eventParams will contain an item for each of the events supported by OnDynamicEvent(). You must handle the Init event and set the value to 1 for each event you want fired.
In the above example we are setting the value of the eventParams dictionary item OnFirstConnection to 1, to indicate that we are going to handle the OnFirstConnection event. OnFirstConnection is an event that is fired just before a new connection is made to an ISP.
eventParams["OnFirstConnection"] = 1;
This is how we tell Hurricane MTA Server to fire OnDynamicEvent(). Below is another way of doing the same thing. It is not as clean looking but is more error proof because the event identifier is being taken from a predefined enumeration.
eventParams[DynamicEventId.OnFirstConnection.ToString()] = 1;
Let’s expand the sample with some more code that will actually do something each time the OnFirstConnection event is fired. First, I need to tell you a bit more about the OnFirstConnection event.
Keep in mind that OnFirstConnection is one of many events that can be handled by the IDynamic interface and was randomly chosen for this example. IDynamic can also be used to handle OnFail, OnSent, etc… as well as other events. The power of IDynamic is that we are now able to add more events, and extend existing events to the plugin API without disturbing backwards compatibility. In that same spirit, all of the old API interfaces such as IBounceProcess and IOutboundSMTPConnection, are all still supported so your old plugins will continue to run as usual.
OnFirstConnection is called when a connection is about to be made to an ISP. The eventParams parameter will contain the following items:
Key | Value |
---|---|
(out string) AccountId | The id of the account making the connection. |
(out string) Domain | The domain the connection is being made to. |
(out string) LocalIp | The local ip address that is connecting out. |
(in/out StringDictionary) DeliveryRule | The delivery rule settings being used. |
By handling the OnFirstConnection event, we can modify the DeliveryRule dictionary object, changing the delivery rule for this ISP in real time.
public class Feedbackloop : PluginBase, IDynamic { public object OnDynamicEvent(DynamicEventId eventId, ref Dictionary eventParams) { //Tell the system we only want to get feedback loop events. if (eventId == DynamicEventId.Init) { eventParams["OnFirstConnection"] = 1; } if (eventId == DynamicEventId.OnFirstConnection) { if (someConditionExists) { StringDictionary rule = eventParams["DeliveryRule"]; rule["Action"]="Defer"; } } return null; } }
In this example, if the variable someConditionExists evaluates to true, then we set the delivery rule action to “Defer”. This changes the delivery rule in the Hurricane MTA Server process to defer all email which is subject to this delivery rule until the action is changed back to “Normal”. Keep in mind that this only affects the delivery rule in memory and not the value in the configuration file for this rule. That means that restarting the server will cause the action to revert back to the value in the config file (since the config file is reloaded when the server starts), which is in most cases “Normal”.
So you might be asking, why would I want to write a plugin to change a delivery rule’s action to “Defer”? Well, one reason would be if you wanted to write some code to monitor the responses you receive from ISPs and change your delivery rules based on those responses. For example you could also handle the OnFail event to monitor the responses and then keep some state as to how you want to change the delivery rule for that ISP the next time a connection attempt is made. If an ISP response indicated that you were being blocked, you might want to automatically start deferring email to that ISP. By coding this way you can add your own strategic logic to the delivery process. Not all of our customers get this involved with the API, but a few power users do – and exploit it to great benefits.
Our goal is to make it possible to extend the software according to customer needs, and each of our customers have different needs. The IDynamic interface allows us to satisfy our customers’ diverse yet important needs today and far into the future.
The IDynamic interface is a part of the current SDK which is included in the latest production version of Hurricane MTA Server released in June ’09. Its documentation and samples can be found in the plugins directory of your Hurricane MTA Server installation.