The Daily Dump

June 06, 2005

.NET | Design Patterns | SevenCamels.Framework.Configuration

Plugin the Provider Pattern

I finally took a look at the Provider Pattern outlined by Rob Howard in MSDN's "Nothin' But ASP.NET" column. There's a Part II as well which comes with a nifty little code example. It's very similar to some work that I've already done with plugins defined in configuration files, so I thought I would abstract my work out a bit more and see if I could get it to support the Provider Pattern as well. Extending it to cater for providers was pretty simple with a little refactoring, but before I start talking about the code, lets take a look at what the Provider Pattern actually is.

The Provider Pattern

At first glance the Provider Pattern seems to be the Plugin Pattern renamed. Take a look at this excerpt from the first article.

[A] provider is the implementation of [an] API separate from the API itself. For example, the new Whidbey Membership feature has a static method called Membership.ValidateUser(). The Membership class itself contains no business logic; instead it simply forwards this call to the configured provider. It is the responsibility of the provider class to contain the implementation for that method, calling whatever Business Logic Layer (BLL) or Data Access Layer (DAL) is necessary.

There are some key concepts in there, mainly the separated interface/contract, multiple implementations to meet the contract, and the specific implementation configured at runtime. This runs very close to the Plugin Pattern by David Rice and Matt Foemmel described in Martin Fowler's book, Patterns of Enterprise Application Architecture. Here's an excerpt from that book describing how to implement the Plugin Pattern.

The first thing to do is define with a Separated Interface any behaviors that will have different implementations based on runtime environment. Beyond that, we use the basic factory pattern, only with a few special requirements. The Plugin factory requires its linking instructions to be stated at a single, external point in order that configuration can be easily managed. Additionally, the linking to implementations must occur dynamically at runtime rather than during compilation, so that reconfiguration won't require a rebuild.

To me the Plugin Pattern basically boils down to a Strategy Pattern with the chosen strategy configured at runtime. The Provider Pattern definitely falls within the realm of the Plugin Pattern as described, but is in fact a more specialised/hybrid version. Take a look at the first excerpt again, specifically at the mentions of an API, business logic layers and data access layers. The provider itself sits between a defined API (the contract/interface) and the business logic / data access layers of an application/subsystem. This is kind of like a reverse Service Layer Pattern. The Service Layer Pattern by Randy Stafford (again from PoEAA) is described as this.

A Service Layer defines an application's boundary and its set of available operations from the perspective of interfacing client layers. It encapsulates the application's business logic, controlling transactions and coordinating responses in the implementation of its operations.

The Service Layer Pattern deals with the allowed operations on an application or subsystem by one or more external clients. It's purpose is to protect the application/subsystem from external clients but also to make it relatively easy to add or switch clients for an application. I say the Provider Pattern is kind of like a reverse Service Layer Pattern because it instead makes it relatively easy to switch business logic and data access code for an application instead. It's theoretically possible to use the Provider Pattern to substitute the entire guts of an application leaving only the client the same; but, in reality, the pattern typically only deals with one small part of the application, like the Membership example given by Rob Howard in the first excerpt. From the perspective of the proposed Membership provider, there is a real service layer involved since the Membership provider would be used by multiple clients. The service layer just happens to be identical across multiple concrete implementations of the Membership provider.

So there you go. The Provider Pattern is unique in that it defines the API for a complex subsystem, with a view to substituting that subsystem through the use of configurable plugins. In this day and age of making applications work with multiple storage technologies and integrating with an infinite variety of other applications, a pattern that deals with this kind of subsystem substitution is helpful. With a consistent set of APIs for common subsystems, applications can even focus on their core proficiency and leave the subsystem implementation to existing libraries, and as a nice little ancillary benefit, remain open to future libraries and technologies without having to recompile.

The SevenCamels.Framework.Configuration Library

Without boring you to tears, here is something for you to actually look at. Included in this download is my new SevenCamels.Framework.Configuration library, recently revamped to support the Provider Pattern, and a small Example program to demonstrate both plugins and providers. The SevenCamels.Framework.Configuration library is licensed under the GNU Lesser General Public License, and the Example program is licensed under the GNU General Public License. The links take you to the human readable summaries of these licenses on the Creative Commons website. I won't bother talking about the licenses other than to say the GNU LGPL allows the free use of the library in any application, commercial or otherwise, with only a few conditions. You're also free to modify the source code.

Okay, lets actually talk about the SevenCamels.Framework.Configuration library. This library primarily deals with the reading of configuration files, specifically the reading of plugin and provider configuration information. This is something it does through custom section handlers, classes that implement the IConfigurationSectionHandler interface. There are two main classes in the library, the PluginSectionHandler and the ProviderSectionHandler. They're very similar to each other in the way they work. The PluginSectionHandler class returns a dictionary collection of instantiated plugins, while the ProviderSectionHandler class returns a dictionary collection of provider configuration information with a view to actually instantiating the providers later on request. I won't get anymore specific than that for now, instead I'll save the details for another post. Let's move on the Example program to see how the library can be used.

The Example Program

To show off both plugins and providers, the Example program will simulate an email gateway service. The idea is to take emails previously saved, apply rules to them, and then send them off. Our provider example will be in the persistence mechanism for the emails, SQL Server or XML file. Our plugin example will be in the rules we apply to the emails. Rules can be written by anyone and "plugged into" the application through the configuration file. We're interested in all rules defined and we only need one instance of each -- perfect for the PluginSectionHandler to deal with.

As you can see in the code, providers still need a lot of work to get going. The library above helps with getting the configuration information out of the configuration file, it doesn't actually do much of what makes up a provider. The first thing to do is define the seperated interface/contract. This can either be an interface like the IPersistenceProvider interface, or an abstract class like the abstract PersistenceProvider class. I've provided both to demonstrate what they would look like. Other implementations of the Provider Pattern can use one or the other (or both) -- Rob Howard seems to favour the abstract class approach which makes sense if there is a lot of common code.

The next thing to do is create a provider factory class. Rob Howard's example code had a factory method on the abstract MembershipProvider class instead, but I prefer to seperate out the responsibility for instantiating the provider from the provider itself. This lets the provider class focus instead on what it's providing and not be cluttered up by factory code. Either approach is fine, it's a personal choice. The factory class reads the provider configuration information and stores it away for when it's needed, similar to the caching code in Rob Howard's example. The factory class also has a static Create method which uses the default provider's configuration information to instantiate it. The new instance of the default provider is then returned to the calling code. Not a lot to it.

That's pretty much all that would be standard between different implementations of the Provider Pattern. Some kind of interface/contract, and some kind of factory process. The rest of it, the actual providers, is academic. In the Example program the SqlPersistenceProvider and the XmlPersistenceProvider classes simply serve up a fake email for demonstration purposes.

So once the Example program has an email, it needs the rules to apply to them. Here the PluginSectionHandler comes into play, reading the plugin configuration information, instantiating the plugins, and returning them as a dictionary collection. The SevenCamels.Framework.Configuration library really makes this easy. Our plugin classes can have multiple public constructors (like the RedirectRule class does) which we can easily switch between simply by changing the number and type of constructor parameters in the configuration file.

In case you're wondering, the rules in the Example program implement the Decorator Pattern (GoF, sometimes called the Wrapper Pattern). The email is wrapped by a rule, which itself is wrapped by another rule, and so on. Each rule exposes the public properties and methods of the object it wraps, altering only those that it needs to (effectively "decorating" it). The rules in the Example program override the properties they're concerned with and may alter the value of the properties depending on the rule itself and its criteria. Take the RedirectRule class for example. If its criteria match (and it always does for demonstration purposes), the To, CC and BCC email addresses are altered -- effectively "redirecting" the email.

To see how easy it is to configure an application that makes use of the Plugin and Provider Patterns, try altering the configuration file. Switch the default persistence provider, comment out rules, even add an extra rule. Try to break the application by altering the types, or by setting up constructor parameters that don't match a public constructor. You should get nice, informative error messages.

And that's all. If you have any questions, feel free to email me at adam@sevencamels.com. If you find any bugs or have any suggestions, please pass them along.

Posted by Adam Boddington at 01:20 PM | Comments (0)

Comments

Post a comment




Remember Me?

(you may use HTML tags for style)