Skip to content

3. Configuration

rycornell edited this page Jan 10, 2018 · 20 revisions

Platibus can be configured programmatically, declaratively, or with a mixture of both styles. In any case, the objective is to initialize an IPlatibusConfiguration implementation that will be passed to the Platibus.Bus constructor. This interface provides access to all of the configuration and dependencies needed by the bus itself.

Additional configuration may be required to configure the bus host itself. In general, these configurations inherit from and extend IPlatibusConfiguration to provide the additional configuration details for the host. Host-specific configuration is described in more detail in the following sections:

  1. Loopback Configuration
  2. HTTP Server Configuration
  3. IIS Configuration
  4. RabbitMQ Configuration
  5. Message Queueing
  6. Message Journaling
  7. Subscription Tracking
  8. Diagnostics

Common Bus Configuration

The Platibus.Bus instance performs the core functions of sending and handling messages. In order to do so, there is a core set of configuration needed by the bus regardless of how it is hosted.

Reply Timeout

When a message is sent the bus creates and stores a reference to an observable ISentMessage object for that message. This object reference is held until all replies to that message are received or a reply timeout occurs. The IPlatibusConfiguration.ReplyTimeout property specifies the maximum amount of time that the sent message reference will be held awaiting a reply.

Declarative example:

<!-- HTTP configuration section -->
<platibus.httpserver replyTimeout="00:05:00">
    <!-- Other configuration ... -->
</platibus.httpserver>

Programmatic example:

public class ConfigurationHook : IConfigurationHook
{
    public void Configure(PlatibusConfiguration configuration)
    {
        configuration.ReplyTimeout = TimeSpan.FromMinutes(5);
    }
}

Serialization Service

The serialization service is specified via the IPlatibusConfiguration.SerializationService property. This object provides ISerializer implementations based on MIME content types. The default implementation, Platibus.Serialization.DefaultSerializationService, provides out-of-the-box support for text/plain, application/json, application/xml, and application/octet-stream content types. Support for additional content types can be added via the DefaultSerializationProvider.Add method.

There is currently no support for declaratively specifying the serialization service. However, a Platibus.IConfigurationHook can be used to override the default serialization service in the PlatibusConfiguration that is generated from the declarative configuration before the bus is initialized.

Programmatic example:

public class ConfigurationHook : IConfigurationHook
{
    public void Configure(PlatibusConfiguration configuration)
    {
        var serializationService = new DefaultSerializationService();
        // Override the default Newtonsoft Json.Net based serializer
        serializationService.Add("application/json", new DataContractJsonSerializer());
        configuration.SerializationService = serializationService;
    }
}

Message Naming Service

To facilitate serialization and deserialization, message object types are assigned a message name that serves as a platform-agnostic type identifier. On the sending side, the message object's type is mapped to a message name which is transmitted to the recipient in the message headers. On the receiving side the message name is mapped onto a suitable type (if applicable) to aid in deserializing the message content. The message naming service is the component that the bus uses to map message object types onto message names and vice-versa.

The message naming service is specified by the IPlatibusConfiguration.MessageNamingService property. The default implementation, Platibus.DefaultMessageNamingService, simply uses the full name of the message type as the message name. An alternate implementation, Platibus.DataContractMessageNamingService, will use the XML qualified name of the data contract for message types decorated with System.Runtime.Serialization.DataContractAttribute and will fall back to the full type name for message types that are not data contracts.

There is currently no support for declaratively specifying the message naming service. However, a Platibus.IConfigurationHook can be used to override the default message naming service in the PlatibusConfiguration that is generated from the declarative configuration before the bus is initialized.

Programmatic example:

public class ConfigurationHook : IConfigurationHook
{
    public void Configure(PlatibusConfiguration configuration)
    {
        var namingService = new DataContractMessageNamingService();
        configuration.MessageNamingService = namingService;
    }
}

Diagnostics

Platibus emits diagnostic events that enable consumers to trace calls, troubleshoot issues, or capture performance metrics. Consumers can access these events by registering a IDiagnosticEventSink with the IDiagnosticService exposed by the IPlatibusConfiguration.DiagnosticService property.

Diagnostic event sinks can also be registered declaratively by adding an element to the <sinks> element collection nested in the '' configuration element.

Declarative example:

<diagnostics>
    <sinks>
        <add name="console" provider="Console"/>
    </sinks>
</diagnostics>

Programmatic example:

public class ConfigurationHook : IConfigurationHook
{
    public void Configure(PlatibusConfiguration configuration)
    {
        var consoleSink = new ConsoleLoggingSink();
        configuration.DiagnosticService.AddSink(consoleSink);
    }
}

Diagnostic event capture is optional and is not enabled by default. Various diagnostic event sinks and their respective configurations are discussed in more detail in the Diagnostics section.

Message Journal

The message journal is an optional component that can be used to track the sending and receipt of messages for diagnostics, compliance, or other purposes. It is specified via the IPlatibusConfiguration.MessageJournal property.

The message journal can be specified declaratively by adding a <messageJournaling> element to the declarative configuration section or programmatically by setting the value of the PlatibusConfiguration.MessageJournal property from a Platibus.IConfigurationHook.

Declarative example:

<messageJournaling provider="SQL" connectionName="MyDB"/>

Programmatic example:

public class ConfigurationHook : IConfigurationHook
{
    public void Configure(PlatibusConfiguration configuration)
    {
        var connectionStringSettings = CongfigurationManager.ConnectionStrings["MyDB"];
        var journal = new SQLJournal(connectionStringSettings);
        configuration.MessageJournal = journal;
    }
}

Because journaling is optional, there is no default configuration, meaning journaling is disabled by default. The examples above demonstrate an implementation has been provided that stores copies of the messages that are sent, received, or published in a SQL database.

Topics

Named topics must be declared before messages can be published to them. This ensures that messages are published to valid topics and enables remote systems to query for lists of topics to which they can subscribe. Topics can be added to the <topics> element of the declarative configuration section or by adding topics to the PlatibusConfiguration via the AddTopic method from an IConfigurationHook.

Declarative example:

<topics>
    <add name="OrderEvents"/>
    <add name="CustomerEvents"/>
</topics>

Programmatic example:

public class ConfigurationHook : IConfigurationHook
{
    public void Configure(PlatibusConfiguration configuration)
    {
        configuration.AddTopic("OrderEvents");
        configuration.AddTopic("CustomerEvents");
    }
}

Endpoints

Endpoints describe Platibus instances to which messages can be sent and from which messages can be received by subscription. Each endpoint must be assigned a unique name that is used elsewhere in the configuration to refer to the endpoint (e.g. send rules and subscription rules).

Endpoint hosts may require authentication in order to send messages. In these cases the endpoint configuration should specify the type of credentials required and additional details (username, password, etc.) as applicable. Currently, only basic authentication and Windows authentication are supported.

Endpoints can be added to the <endpoints> element of the declarative configuration section or by adding endpoints to the PlatibusConfiguration via the AddEndpoint method from an IConfigurationHook.

Declarative example:

<endpoints>
    <add name="provisioning-system" address="http://prov.example.com/platibus" 
         credentialType="Basic" username="user" password='trustno1'/>
    <add name="crm-system" address="http://crm.example.com/platibus" 
         credentialType="Windows" />
</endpoints>

Programmatic example:

public class ConfigurationHook : IConfigurationHook
{
    public void Configure(PlatibusConfiguration configuration)
    {
        configuration.AddEndpoint("provisioning-system",
            new Endpoint(new Uri("http://provisioning.example.com/platibus"),
                new BasicAuthCredentials("user", "trustno1")));
        configuration.AddEndpoint("crm-system",
            new Endpoint(new Uri("http://crm.example.com/platibus"), 
                new DefaultNetworkCredentials()));
    }
}

Send Rules

When messages are sent they are delivered to the distinct set of endpoints for which there are send rules that apply to the message. The criteria for determining whether a send rule applies to a message are encapsulated in IMessageSpecification objects that implement a single IsSatisfiedBy method. Any rules with message specifications that return true for a given message apply to that message, and the message will be delivered to the associated endpoints.

While declarative configuration is supported for send rules, the message specifications are currently limited to a regular expression matches on the message name. If more complex specifications are required (for example, matching on other message headers or message contents) then the send rules can be added programmatically to the PlatibusConfiguration via the AddSendRule method from within an IConfigurationHook.

Declarative example:

<sendRules>
    <add namePattern=".*Order.*Command" endpoint="provisioning-system"/>
    <add namePattern=".*Customer.*Command" endpoint="crm-system" />
</sendRules>

Programmatic example:

public class ConfigurationHook : IConfigurationHook
{
    public void Configure(PlatibusConfiguration configuration)
    {
        configuration.AddSendRule(new SendRule(
            new MessageNamePatternSpecification(".*Order.*Command"),
            "provisioning-system"));
        configuration.AddSendRule(new SendRule(
            new MessageNamePatternSpecification(".*Customer.*Command"),
            "crm-system"));

        // More complex examples

        // Send any message whose content contains the string "error"
        // to the "error-logging" endpoint
        configuration.AddSendRule(new SendRule(
            new DelegateMessageSpecification(m => m.Content.Contains("error")),
            "error-logging"));

        // Send any message containing anything resembling a credit
        // card number to both the "data-loss-prevention" and the
        // "compliance" endpoints
        configuration.AddSendRule(new SendRule(
            new DelegateMessageSpecification(m => 
                Regex.IsMatch(m.Content, "(^|\b)\d4(?:-\d4){3}(\b|$)")),
            "data-loss-prevention", "compliance"));
    }
}

Subscriptions

When messages are published to a topic they are delivered to all subscribers of that topic. Subscribers request receipt of published messages via subscriptions. Each subscription indicates the endpoint and topic being subscribed to as well as optional "time to live" or TTL.

Subscription rules can be specified declaratively via the <subscriptions> collection or registered programmatically to the PlatibusConfiguration via the AddSubscription method from within an IConfigurationHook.

Declarative example:

<subscriptions>
    <add endpoint="provisioning-system" topic="OrderEvents" ttl="12:00:00"/>
    <add endpoint="crm-system" topic="CustomerEvents" />
</subscriptions>

Programmatic example:

public class ConfigurationHook : IConfigurationHook
{
    public void Configure(PlatibusConfiguration configuration)
    {
        configuration.AddSubscription(new Subscription(
            "provisioning-system", "OrderEvents", TimeSpan.FromHours(12)));
        configuration.AddSubscription(new Subscription(
            "crm-system", "CustomerEvents"));
    }
}

Subscription Time to Live (TTL)

If a TTL is specified the subscribing application will renew its subscription periodically at an interval that is half the TTL. If the publishing application does not receive a subscription renewal request by the time the TTL elapses, it will cease sending publications to the subscriber. In other words, if the subscription rule is removed and the subscribing application does not send an explicit request to unsubscribe, the subscription will eventually expire and be removed by the publishing application.

If a renewal request is unsuccessful, the subscribing application will retry at an interval of 30 seconds until it succeeds.

Subscriptions with no TTL do not expire and are not remove without an explicit request to unsubscribe.

Configuration Hooks

Configuration hooks are used to supplement declarative configuration during bus initialization. Implementing a configuration hook is as simple as including a concrete implementation of Platibus.Configuration.IConfigurationHook with a default constructor in one of the application assemblies (exe or dll). Multiple configuration hooks can be provided and each will be called. However, the order in which multiple configuration hooks are invoked is unspecified. Configuration hooks do not need to be thread-safe.

Configuration Hooks and Bus Initialization

When the bus is initialized, the declarative configuration is read and used to construct and initialize a PlatibusConfiguration instance. Once the PlatibusConfiguration object has been initialized, the configuration manager scans the assemblies in the app domain base directory (as well as in the bin subdirectory, if one exists, e.g. for web apps) for concrete implementations of IConfigurationHook. For each type that is found, the configuration manager attempts to instantiate the type using the default constructor, and then passes the PlatibusConfiguration object to the configuration hook instance. The same PlatibusConfiguration instance is passed to each hook, so the changes made to the PlatibuscConfiguration are cumulative.