I often hear about the Cloud as the Messiah these days. Cloud should not be seen as a bad architecture saver; it is not. Bad architecture or approach will remain ‘bad’ under any circumstances, but linked with a Cloud you will faced significant costs. Cloud is about elasticity enabled through the pooled resources. If you don’t achieve high utilization, you are paying for nothing. High utilization of machines should be given as well, but how can we achieve this? The idea is simple; we are specializing work, instead of worker.

The “worker” in context of Windows Azure (now Windows Azure Cloud Services) is the Worker Role. The Worker Role is the beast which is continuously looking for a new job to be processed, similar to a Windows Service. But how to deliver a job do be done in flexible way and independently? The idea is simple, with the help of plug-ins.

Update 14. February 2013: In the Plug-Ins infrastructure I implemented in this sample was one big design issue. If you downloaded the code before this date, please take the new one. Read this blog entry for more details.

Update 10. March 2013: In the class PluginBase was overridden the method InitializeLifetimeService. This method is returning null, to disable remoting lifetime service. If you don’t do this you will get, after couple of minutes, the Remoting Exception “Object […] has been disconnected or does not exist at the server.” when the plugin’s instance is not be used. This is not the best idea for your production code, because the plugin’s instance is staying in memory until will not be explicitly unloaded from.

Plug-in is a part of code provided by a developer which describes the work to be done. In contrast to Wikipedia’s definition of a plug-in where a plug-in is described as a part which is customizing the main functionality, the situation is different here. In this sample each plug-in is a main part of the functionality and the rest can be seen as infrastructure.

In this sample a plug-in is seen as main part of the functionality in contrast to common consensus.

To get an idea of what I’m talking about take a look on the video below.

Plug-Ins Infrastructure and Management

For the plug-in management my own implementation is applied but you can use MEF (Microsoft Extension Framework) or any other framework instead. This plug-in infrastructure is a general one and usable in every type of project.

PluginBase is a base class for all concrete plug-ins. This class has no members and the only function in this sample is to ensure that concrete plug-in inherits from MarshalByRefObject.

MarshalByRefObject enables access to objects across application domain boundaries in applications that support remoting.
MSDN

Why do we need this? Our plug-ins are running in separate application domains. The application domain is isolating one running code from another and enables us to exchange the plugin-in assemblies on runtime or eventually unload them from memory if they are not used anymore. Because we need access to the instance of those plug-ins from the primary application domain without creating a copy, we must ensure that proxies will be created, so that we can use one object per reference. This approach should be fine because plugin-ins are running usually on the same server so we don’t produce any network traffic.

A concrete plug-in is deriving from an abstract PluginBase class. If you would like to create some base class for all your plug-ins, you can inherit from PluginBase. Each plug-in must have a PluginMetadataAttribute. The PluginMetadataAttribute additionally describes a plug-in with a name and a version.

Plugin Infrastructure

Imagine we would have a couple of plug-ins for text manipulation and each plug-in should implement the method string Manipulate(string input). We create a base abstract class TextManipulationPluginBase and each concrete plug-in is derived from this one.

RightToLeft Plugin

Each plug-in is loaded in PluginContainer. In the container a reference to the plug-in’s application domain, the instance of the plug-in self and his metadata is stored.

PlugIn Container

After loading assemblies from a file system using Assembly.LoadFrom it is necessary to filter out types, which don’t obtain necessary characteristics. A plug-in in our sample is only one, which is inherited from a PluginBase class (achieved with generic constraint),  has a PluginMetadataAttribute and the same assembly as <TPlugin>.

        /// <summary>
        /// Gets the matching plugin types (which are of type 'TPlugin' and have set 'PluginMetadataAttribute')
        /// </summary>
        /// <param name="types">The types.</param>
        /// <returns></returns>
        private IEnumerable GetMatchingPluginTypes(IEnumerable types)
        {
            return types
                .Where(type => type.BaseType != null &&
                               type.BaseType.AssemblyQualifiedName == typeof(TPlugin).AssemblyQualifiedName &&
                               type.GetCustomAttributes(typeof(PluginMetadataAttribute), true).Any());
        }

Now  we need to create a new application domain for each plug-in and store them within plug-in in its container.

Update 14. February 2013: We need to create new application domain for each plug-in but in different way. The method LoadPluginsFromAssembly in the class PluginManager is called from main application domain. Before I creating new application domain, I’m calling the method GetAllTypesFromAssembly. This  method is loading all types from assembly with help of framework method Assembly.LoadFrom. And this is the central problem because the method Assembly.LoadFrom is loading all types to the same application domain where the invocation occurred. There are two major problems with this:

  1. Assemblies are loaded in memory twice (in the main application domain and later into the new application domain)
  2. Assemblies get be locked because you can’t unload assemblies from main application domain
        ///
        //// Creates the new app domain for file.
        ///
        ///The plugin file path.
        ///
        private AppDomain CreateNewAppDomain(string pluginFilePath)
        {
            var appDomainName = Guid.NewGuid().ToString();
            var appDomainSetup = new AppDomainSetup
            {
                ApplicationName = appDomainName,
                ApplicationBase = Path.GetDirectoryName(pluginFilePath),
                ShadowCopyFiles = "true" //shadow copy because to be able to replace assemblies even if loaded in memory
            };

            return AppDomain.CreateDomain(appDomainName, AppDomain.CurrentDomain.Evidence, appDomainSetup);
        }



        ///
        /// Loads the plugins from assembly.
        ///
        ///The assembly path.
        ///
        /// pathToPlugins
        ///
        private IEnumerable LoadPluginsFromAssembly(string assemblyPath)
        {

            IList loadedPlugins = new List();

            //load all types from assembly
            IEnumerable types = GetAllTypesFromAssembly(assemblyPath);

            //load only types which are of type  and have set 'PluginMetadataAttribute'
            //type.IsSubClassOf(typeof(MapBase)) --> always returns false, because: MapBase is loaded by the framework and by Assembly.Load
            //for further information see: http://stackoverflow.com/questions/3623358/two-types-not-equal-that-should-be
            IEnumerable matchingPluginTypes = GetMatchingPluginTypes(types);

            foreach (Type matchingPluginType in matchingPluginTypes)
            {
                var appDomain = CreateNewAppDomain(matchingPluginType.Assembly.Location);
                var mapAttribute = (PluginMetadataAttribute)matchingPluginType.GetCustomAttributes(typeof(PluginMetadataAttribute), true).Single();
                var concretePlugin = (TPlugin)appDomain.CreateInstanceAndUnwrap(matchingPluginType.Assembly.FullName, matchingPluginType.FullName);

                var plugin = new PluginContainer(concretePlugin, mapAttribute, appDomain);
                loadedPlugins.Add(plugin);
            }

            return loadedPlugins;
        }

The way to address the issue described upon is to extract the code which is loading the plugins, into the new class FileSystemPluginLoader. This class will be instantiated from PluginManager into new application domain.

image

The code which is loading FileSystemPluginLoader to the new application domain can you see below.

        /// <summary>
        /// Loads the plugins from store.
        /// </summary>
        ///uri">The URI.
        /// <returns></returns>
        /// <exception cref="System.ArgumentException">uri</exception>
        /// <exception cref="System.IO.FileNotFoundException"></exception>
        private IEnumerable LoadPluginsFromStore(string uri)
        {
            if (String.IsNullOrWhiteSpace(uri))
            {
                throw new ArgumentException("uri");
            }

            var privateBinPath = Path.Combine(_relativePluginsDirectoryPath, Path.GetFileName(Path.GetDirectoryName(uri)));
            var pluginManagerAppDomain = CreateNewAppDomain(privateBinPath);
            var pluginLoader = (IPluginLoader) pluginManagerAppDomain.CreateInstanceAndUnwrap(
						Assembly.GetCallingAssembly().FullName, typeof (FileSystemPluginLoader).FullName);
            var loadedPlugins = pluginLoader.LoadPlugins(uri);
            return loadedPlugins;
        }

On the line 16 is created new application domain. On the line below is instantiated new instance of FileSystemPluginLoader. On the line 18 is called the code which is loading plugins from file system. There is only little difference to the code which we used before to loading plugins. The main difference is that we don’t need to create the new application domain here.

        public System.Collections.Generic.IEnumerable LoadPlugins(string uri)
        {
            if (!File.Exists(uri))
            {
                throw new FileNotFoundException(string.Format("Could not find file ('{0}').", uri));
            }

            IList<PluginContainer<TPlugin>> loadedPlugins = new List<PluginContainer<TPlugin>>();

            //load all types from assembly
            IEnumerable types = GetAllTypesFromAssembly(uri);

            //load only types which are of type <TPlugin> and have set 'PluginMetadataAttribute'
            //type.IsSubClassOf(typeof(MapBase)) --> always returns false, because: MapBase is loaded by the framework and by Assembly.Load
            //for further information see: http://stackoverflow.com/questions/3623358/two-types-not-equal-that-should-be
            IEnumerable matchingPluginTypes = GetMatchingPluginTypes(types);

            foreach (Type matchingPluginType in matchingPluginTypes)
            {
                var pluginMetadata = (PluginMetadataAttribute)matchingPluginType.GetCustomAttributes(typeof(PluginMetadataAttribute), true).Single();
                var plugin = (TPlugin)AppDomain.CurrentDomain.CreateInstanceAndUnwrap(matchingPluginType.Assembly.FullName, matchingPluginType.FullName);

                var pluginContainer = new PluginContainer(plugin, pluginMetadata, AppDomain.CurrentDomain);
                loadedPlugins.Add(pluginContainer);
            }

            return loadedPlugins;

        }

A word about creating a new application domain: There is a one major important change. In the method CreateNewAppDomain is now via parameter possible to set PrivateBinPath. PrivateBinPath is a (relative) path to the ApplicationBase path and represent directories that are probed for private assemblies. ApplicationBase path is a path to the assembly where  FileSystemPluginLoader is located. Typically is this the applications  bin directory. The directories with the plugins must be located relative to this directory and the PrivateBinPath is the directory with a concrete plugin.


        /// <summary>
        /// Creates the new app domain.
        /// </summary>
        /// <param name="pluginFilePath">The plugin file path.</param>
        /// <returns></returns>
        private AppDomain CreateNewAppDomain(string pluginFilePath = null)
        {
            var appDomainName = Guid.NewGuid().ToString();
            var appDomainSetup = new AppDomainSetup
                {
                    ApplicationName = appDomainName,
                    ApplicationBase = _applicationBasePath,
                    ShadowCopyFiles = "true", //shadow copy because to be able to replace assemblies even if loaded in memory
                    PrivateBinPath = pluginFilePath
                };

            return AppDomain.CreateDomain(appDomainName, AppDomain.CurrentDomain.Evidence, appDomainSetup);
        }

For example in the case of our PluginsTestConsole:

“…\CloudServicesPlugInSample\PluginsTestConsole\bin\Release” is the ApplicationBase “

“…\CloudServicesPlugInSample\PluginsTestConsole\bin\Release\Plugins\RightToLeft” is the PrivateBinPath for the plugin RightToLeft

You can download the complete solution from here. The next part is going to be about how to use this plug-in infrastructure in Windows Azure. Stay tuned.