Introduction
Why do we need a plugin framework?
The need of time has pointed developers towards rapid component development and plug-n-play architectures. The industry is looking towards enterprise product portfolio
integration and/or some form of ESBs. I have heard several talks from industrial king-pins trying to prove apples as oranges (or at least a new red color
hard cousin of orange which can internally look and feel just like orange but gives so much more benefit). Such thoughts,
I agree, can either come from someone under severe constipation or from look-I-am-Einstein brains. But the arguments are also not
entirely false. For product survival it has becomes very important to be adaptive, extensible and polymorphic in as many places as possible.
This is so much more true in case of small-to-mid size organizations targeting small-to-mid-size market.
Another very interesting business need is compartmentalizing multiple business logic processes, across physically diverse development groups and then integrate them
in the same assembly line. Even in such context using a plugin mode of separation becomes important.
Background
I've suffered (working) for several months in 'enterprise application-portfolio integration platforms' where everyone expects a plugin to be no less than Albus Dumbledore,
I have learnt, rather in a hard way, to propose using a plugin management platform uniformly across multiple integratable applications. There is rather no compulsion
to use it at the beginning, but as soon as you think of a plugin architecture, think of uniforming them.
This article is targeted at such requirements where it can give a quick start towards plugin hosting, management and integration.
By using such a framework, as a developer, you are only writing your custom BL and not bother to dirty your hand.
How does it work?
Event based plugin interaction/invocation
Times have changed. Now a plugin is expected to be no less than a superhuman.
- It needs to understand host application gestures
- It needs to work in the background (async)
- It needs to raise and subscribe to platform defined events
- It needs to change when a newer version needs to be deployed
- It needs to integrate with host of other invocation channels
So the article proposes the plugin to be event based. The plugin handshakes with the host platform during initialization. And such a handshake is composed of a mutual two-way
subscription to the interesting events. This handshake would live until the plugin is living in the host scope/lifetime. Some initialization is still a simple invocation but these,
in principle, could also be graduated to events. My personal take is, an event based communication is only necessary when its occurrence (or its time frame) is uncertain.
Actions such as initialization is generally obvious and can be spared from the overhead from an event based invocation.
As Spiderman said, "With power comes responsibility", I am too small an entity to prove it wrong. So here comes your responsibilities starting with using Dispatcher object
when you use this (which mostly will be the case) in an UI intensive application(s).
Managed Extensibility Framework (MEF)
A bit of a cakewalk and hand-washing from the good old days' "dirty work". A good or great part of hosting plugins is to load them. A few years back developers had to jump,
catch and yell (in short a circus effort) to reflectionalize object creation. And then there was MEF (http://mef.codeplex.com/). In short it helped to quickly and dynamically
wrap the plugin instance creating pain. With several other nifty utilities here this one proved quite a blessing. Our PluginManager internally uses a factory here which serves
as the heart of the entire system and the heart's first beat looks like this:
public void Compose()
{
var catalog = new AggregateCatalog();
catalog.Catalogs.Add(new DirectoryCatalog(extensionsPath));
Directory.GetDirectories(extensionsPath).ToList().ForEach(
p => catalog.Catalogs.Add(new DirectoryCatalog(p)));
container = new CompositionContainer(catalog);
container.ComposeParts(this);
}
There are many things which comes free, one of the main ones is automatic dependency resolving from multiple disjoint probing paths.
The class is hidden behind a short interface (in case/if/when there is a day when MEF is runs obsolete).
public interface IComposition<T>
{
void Compose();
void Decompose();
IEnumerable<T> ComposedItems { get; set; }
}
With these serving as the boiler plate code the rest of the hosting code kicks off...
The Plugin Host
The term host is a little blown up here, may be a container would sound more appropriate. But again this does more than container. These few classes are responsible for:
- Load plugin assemblies (it can be configured 'from where' - I have used the users
%APPDATA% folder)
- Serves as a one stop shop from standard application to plugin queries
A sample WPF window is also added to demonstrate the primary usage of the Plugin-Platform. Please note that the sampleHost only does the bare minimum
to load plugins. There are several other features to be explored (documented below).
Here are the two most important methods of the host:
private bool RegisterPluginHandshake(IEnumerable<PluginInfo> plugins)
{
return pluginMgr.RegisterExtensions(this,
PluginNotifyMessage, GetUserContext(), plugins);
}
private void UnRegisterPluginHandshake(IEnumerable<PluginInfo> plugins)
{
pluginMgr.UnLoadExtensions(this, PluginNotifyMessage, plugins);
}
A little deeper into the Plugin Platform...
Here is a functional diagram of the scope of the platform. The mentioned interface names might differ in attached code. But it should be easy to determine which are the ones.
Below is the interface that manages the event contract.
public interface IApplicationEvents
{
event EventHandler OnApplicationStart;
event EventHandler OnApplicationClose;
event EventHandler OnModuleInitalize;
event EventHandler OnFileSystemEvent;
event EventHandler OnActivateEvent;
}
public interface IExtensionEvents
{
event EventHandler OnStarted;
event EventHandler OnCompleted;
event EventHandler OnError;
}
It is important to note here that the application and the plugins both are event sources themselves. While the platform is free to decide what kind of a event it wants
to be part of the domain, the plugins are limited to raise only those events that the platform has pre-decided to be listened by itself. This in no way should be understood
as a heavy-handed decision or a medium to control. There could be a host of interesting events and all of them could be made available under multiple versions/releases.
Please check the illustration below.
Using the code
The code in the framework is straight-forward and it is possible to just work with
Common.plugin.Platform.dll (unless you need to change the framework or debug).
Two basic plugins are included in the solution to get you started.
- KickStartPlugin - As stupid as the name could be it just listens to the right click context menu item event from Windows Explorer
and responds with another parallel event. This happens through the singleton application instance (the code for which is included but beyond the scope of discussion in context
with plugin integration)
- TimeEventPlugin - Another equally stupid use-case. This plugin internally uses a timer to raise events every
10 seconds to bog the application out (just for your interest).
Below is a normal screenshot of the application.
Points of Interest
To summarize the framework (or the idea) is targeted at
- to publish your application and let independent software/teams create custom functionality to extend your application externally
- integrating multiple business processes in the same assembly line (if need be).
- manage complex development functions but segregating efforts and then merging the application
under a container
Future
- COM integration for third party COM app.
- Implicit authentication with logged-in or ActiveDirectory user.