Transport Agent Factories

This content is no longer actively maintained. It is provided as is, for anyone who may still be using these technologies, with no warranties or claims of accuracy with regard to the most recent product version or service release.

Topic Last Modified: 2008-09-22

By Saeed Noursalehi, Software Development Engineer, and Ray Dixon, Programming Writer

Where do instances of transport agents come from? Why, transport agent factories, of course! In this article you will learn about the following:

  • Agent factories
  • Agent lifetime of SmtpReceiveAgents and RoutingAgents
  • Advanced usage patterns for agent factories, including:
  • Managing agent configuration
  • Using the SmtpServer class

Agent Factories

A transport agent is implemented as a class that derives from either the SmtpReceiveAgent Class or the RoutingAgent Class. Every agent also needs a corresponding agent factory that derives from the SmtpReceiveAgentFactory Class or the RoutingAgentFactory Class, respectively. The agent factory is the type that is specified when the agent is installed, and it is the only object that the transport code knows about. When transport has to create an instance of the agent, it actually calls into the CreateAgent method of the agent factory to create that instance.

The basic pattern is shown in the following example. The pattern for a RoutingAgent is also very similar.

    public class SampleSmtpReceiveAgentFactory : SmtpReceiveAgentFactory
    {
        public override SmtpReceiveAgent CreateAgent(SmtpServer server)
        {
            return new SampleSmtpReceiveAgent();
        }
    }

    public class SampleSmtpReceiveAgent : SmtpReceiveAgent
    {
        public SampleSmtpReceiveAgent()
        {
            // Register for any events you need here.

            // For example:
            this.OnEndOfData += 
                new EndOfDataEventHandler(this.EndOfDataHandler);
        }

        private void EndOfDataHandler(
            ReceiveMessageEventSource source,
            EndOfDataEventArgs e)
        {
            // Handle the event as appropriate.
        }
    }

Agent Lifetime

Each agent instance that is obtained by transport from the CreateAgent() method is only used for the duration of the session or message that it was created for.

Note

The agent factory must create a new instance of the transport agent each time the CreateAgent() method is called. Exchange stores state on the agent instance and does not support the concurrent reuse of agent instances.

For an SmtpReceiveAgent, the agent instance is created when the SMTP session is first created, and the reference to it is released after the session is closed. It is possible to get many SMTP commands and to receive several messages on that session, and all the associated events will be handled by the same agent instance. However, after the session is closed, that agent reference is no longer used.

For a RoutingAgent, the agent instance is created when a message is removed from the submission queue and enters the categorizer pipeline. The same agent instance is then used for all events on that message and any messages that fork from that message. After the message and all its forks have been deferred, deleted, or sent to a delivery queue, that agent instance is no longer used.

Advanced Usage Patterns

Some advanced usage patterns for transport agent factories include managing agent configuration, using the SmtpServer Class, and managing the lifetime of your agent.

Managing Agent Configuration

One common pattern is to load the configuration of the agent in the constructor of the agent factory, and pass a reference to the configuration to each agent instance that is created. This enables you to maintain a single instance of the configuration and only load it once, but share it across all instances of your agent.

It also allows for a pattern where the agent factory registers for change notifications and updates its configuration without interfering with agents that are already running. To do this, the agent factory loads its configuration at startup and maintains a reference to its configuration object, which it hands to every agent instance it creates. When a change notification arrives, the agent factory creates a new configuration object and replaces the old one. This enables existing agent instances to continue using the old configuration and not have to worry about the configuration changing while the agent is running. However, all new agents will receive the new configuration.

The following example shows the basic pattern.

    public interface IConfiguration
    {
        // Define appropriate methods and properties here.
    }

    public class SampleSmtpReceiveAgentFactory : SmtpReceiveAgentFactory
    {
        private IConfiguration configuration;

        public SampleSmtpReceiveAgentFactory()
        {
            this.Initialize();
        }

        public override SmtpReceiveAgent CreateAgent(SmtpServer server)
        {
            return new SampleSmtpReceiveAgent(this.configuration);
        }

        private IConfiguration LoadConfiguration()
        {
            // Load configuration from the appropriate place here.
            // Also, register for a change notification and pass in
            // this.HandleChangeNotification as the change handler.

            return null;
        }

        private void HandleChangeNotification()
        {
            this.Initialize();
        }

        private void Initialize()
        {
            this.configuration = this.LoadConfiguration();
        }
    }

    public class SampleSmtpReceiveAgent : SmtpReceiveAgent
    {
        private IConfiguration configuration;

        public SampleSmtpReceiveAgent(IConfiguration configuration)
        {
            this.configuration = configuration;

            // Register for any events you need here.

            // For example:
            this.OnEndOfData += 
                new EndOfDataEventHandler(this.EndOfDataHandler);
        }

        private void EndOfDataHandler(
            ReceiveMessageEventSource source,
            EndOfDataEventArgs e)
        {
            // Handle the event as appropriate.
            // Use the configuration object as needed to handle this event.
        }
    }

Using the SmtpServer Class

The SmtpServer class gives agents access to the IP permissions table, the address book, the accepted domains and remote domains tables, and a method for submitting new messages. This object is passed to the CreateAgent() method of the agent factory, and therefore has to be passed to each instance of the agent if the agent has to have access to its functionality.

Conclusion

Now that you know about transport agent factories and how they work with transport agents, go build some factories. Crank out some agent instances and have some fun!