In my previous posts I developed simple sender and receiver classes for topics and queues using Apache.NMS and ActiveMQ. In this post I will use the provider pattern to simplify creation and management of NMS connections, senders and receivers. Additionally, I’ll improve the syntax and ease of using NMS by adding fluent-interface behavior to the sender and receiver classes previously developed in part 1 and part 2, as well as a little refactoring to better manage the lifetime of the objects.
Provider Pattern Background
The provider pattern is a software pattern developed at Microsoft during the development of ASP.NET 2.0 and used pretty extensively in that release. The most noticeable implementation was the Membership API, where the data storage of your security was abstracted away from the implementation. The aim is the pattern is loose coupling similar to what is provided by an IoC and DI, such as Ninject or Windsor container via settings in a configuration file as injected parameters.
There has been a bit of debate over whether or not it is a true pattern with most people agreeing that it’s a really several patterns or simply another pattern with a different name; most notable the Factory and Strategy patterns in some circles and the Singleton or Bridge patterns in others. The Bridge pattern definition most closely matches the goals of the pattern:
"decouple an abstraction from its implementation so that the two can vary independently"
- Gamma, Helm, Johnson, and Vlissides (gof)
As far as I am concerned it is a sort of hybrid pattern; a composition of patterns if you will.
Provider Pattern Implementation
The provider pattern itself is deeply ingrained within .NET itself; there is even a namespace with base classes supporting the pattern – System.Configuration.Provider. This namespace contains the following base classes, BaseProvider and ProviderCollection which are the building blocks of configuration based providers. System.Configuration adds another abstract class that makes working with .NET configuration files easier. Note that you could in theory create your own provider implementation without using these classes, but they provide an easy to follow framework for making providers using .NET.
The following diagram illustrates the entire provider implementation:
Here is a rundown of the various classes required:
- NmsProvider – derives from BaseProvider and provides initialization for the provider plus factory methods for creating NMS senders and receievers. Only one instance of a configuration should exist for an AppDomain or process and all operations should be thread safe.
- NmsProviderCollection – derives from ProviderCollection and maintains a list of configured providers. For example you could have providers configured for multiple ActiveMQ servers i.e. receive from one server and send to another.
- NmsProviderConfigurationSection – provides a defined section in your configuration file for each provider implementation.
- NmsProviderService – a singleton ‘service’ for maintaining the collection of providers while your application is running. Note that providers are long lived objects, hence the need for a singleton to maintain the list.
The NmsProvider class is really a façade encapsulating the interaction between the JMS broker and the client’s it services. From a client perspective it manages the connection between the client and the JMS broker, the sessions that are generated per conversation, as well as the creation of objects for sending JMS messages to receiving messages from destinations (queues and topics). An important point to remember is that JMS Connections are heavyweight, long lived objects and are typically created 1:1 with clients, which “fits” well with the lifecycle of providers.
All providers use configuration files to store the type of provider to initialize and any additionally information that must be injected into the newly created instance. Note that this is a form of Dependency Injection (DI). The configuration file may contain configurations for multiple instances of each provider configured differently. For example, each provider may be using a different broker to send or receive messages. Here is a listing of a configuration that lists several provider instances using the same broker:
When the NmsProviderService is created, it will read the contents of NmsProviderConfigurationSection and construct providers for each entry in the “providers” section; in this case two providers are listed: NmsProvider0 and NmsProvider1. The System.Configuration namespace contains a special class to help in creating the providers called ProvidersHelper. After the providers are created they are added to the NmsProviderCollection, which is a thread safe collection of providers.
When the provider is created by the ProvidersHelper.InstantiateProviders method in the NmsProviderService class, the overriden ProviderBase.Initialize method will be called in the NmsProvider class. The Initialize method will assign the values stored in the configuration file to their respective properties on the NmsProvider class and then create an instance of the Apache.NMS.ConnectionFactory class.
As previously discussed, the NmsProvider is façade encapsulating the interaction between the JMS server and the clients that it services. It is also a factory in that it offers methods for creating objects for sending and receiving messages. Notable it hold references to Apache.NMS.IConnection and Apache.NMS.IConnectionFactory objects. The IConnectionFactory class is a factory for creating IConnection instances based on the configuration supplied.
Clients use providers by name via and indexer on the NmsProviderService after calling the Start() method on the provider to create the connection between the client and the JMS server:
The Start() method creates a new IConnection if the provider does not have one already initialized. Then it checks to see if the IConnection is started, if not then the IConnection’s Start() method is called which establishes the connection with the JMS server. Additional methods on the provider allow the IConnection to be temporarily stopped or permanently closed:
Once the provider has been initialized, the usage is quite simple:
**Make sure you have ActiveMQ running and your configuration file is configured with the appropriate uri. See http://rantdriven.com/post/ActiveMQ-via-C-sharp-and-dotnet-using-ApacheNMS-Part-1.aspx for details on how to get ActiveMq up and running on a windows box.