Writing a plugin for Pluto

Yesterday I finished porting Jade Virtual File System to Pluto. It involved mostly getting rid of all the "J" in the class names (JFilesSource to FilesSource and so on), some namespace clashes (System.IO.Directory with Pluto.VFS.Storage.Directory for example), and some new extra features to transform the VFS into a fully working Pluto plugin.

First, let's talk about Pluto: it stands for PLUgin TOolkit, and as it's name tells is a toolkit to give plugins support to any application. Pluto is not tied to the engine, although it was born from the work Reed and Quimbo did in Jaded. Our idea in our next Jade release is to use Pluto extensively to give us extensibility and flexibility. Pluto also comes with several base classes and tools to help you develop new plugins.

Pluto works publishing a set of services to the outside world, so other apps can use these services when needed. That means that the VFS must become a service in Pluto so other applications can query it. Let's see how to create and publish it in Pluto:

#region Using Directies

using Pluto.Core;
using Pluto.Configuration;

#endregion

namespace Pluto.VFS
{
/// <summary>
/// Exposes an <see cref="IVFSService"/> service.
/// </summary>
[CompletionStatus(100)]
[ExposedService(typeof(IVFSService))]
[ExposedService(typeof(IFilesSourceBuilderService))]
[ServiceDependency(typeof(IConfigurationService))]
public class VFSServicePlugin : ServicePluginBase
{
#region Methods

/// <summary>
/// Registers the <see cref="IVFSService"/>.
/// </summary>
protected override bool RegisterServices(IPlutoServiceContainer serviceContainer)
{
serviceContainer.AddService<IVFSService>(new VFSService(serviceContainer));

serviceContainer.AddService<IFilesSourceBuilderService>(new HardDiskSourceBuilder(), "HardDisk");
serviceContainer.AddService<IFilesSourceBuilderService>(new StorageSourceBuilder(), "Storage");

return true;
}

#endregion
}
}

 

Let's review the important (and probably a little strange right now) part of this code:

  • VFSPluginService : PluginBase: all plugins in pluto must inherit from this class. When Pluto loads an assembly it queries for these classes to load their declared services.
  • [ExposedService(typeof(IVFSService))] and [ExposedService(typeof(IFilesSourceBuilderService))] : these are the services that this plugin declares to the outside world. As you can see in the method RegisterServices I add them to Pluto ServiceContainer so other classes will be able to use them later. The services are declared as interfaces, so people can do and use different implementations of the same service and aren't tied to the default one we give in Pluto.
  • [ServiceDependency(typeof(IConfigurationService))]: I also tell Pluto that our plugin will need the IConfigurationService to work. When a plugin depends on other plugins you should use this attribute so Pluto knows the right order to load plugins.
  • [CompletionStatus(100)]: this attribute is a development attribute that tells us that this class is 100% done now. Pluto comes with an assembly browser that uses this attribute to see what classes, methods,.. are done, documented, tested and so on. It's pretty useful to know what's the status of things when a project gets big.

 That's the plugin part. You can see it's pretty easy to create a plugin in Pluto. There are some other small changes in the VFS main code (in the constructor mainly, so I'll only post that code).

namespace Pluto.VFS
{
/// <summary>
/// Represents a virtual file system.
/// </summary>
/// <remarks>
/// The priority in the searches within this object is related to the order in which the
/// <see cref="IFilesSource"/> items are aggregated. The first item gets higher priority
/// and so on.
/// </remarks>
[CompletionStatus(100)]
public class VFSService : IVFSService
{
#region Constructors

/// <summary>
/// Default constructor.
/// </summary>
/// <remarks>
/// Uses the <see cref="IConfigurationService"/> to load itself
/// from the configuration.xml file.
/// </remarks>
internal VFSService(IPlutoServiceContainer serviceContainer)
{
_serviceContainer = serviceContainer;

IConfigurationService configuration = _serviceContainer.GetService<IConfigurationService>();

// Read the VFS main values
_name = configuration.ReadValue<string>("VFS", "Name", string.Empty);
_path = configuration.ReadValue<string>("VFS", "Path", string.Empty);

// Get all sources and load them
string[] keys = configuration.GetPaths("VFS/FilesSource", "Name");
foreach (string key in keys)
{
string type = configuration.ReadValue<string>(key, "Type", string.Empty);
string name = configuration.ReadValue<string>(key, "Name", string.Empty);
string path = configuration.ReadValue<string>(key, "Path", string.Empty);

IFilesSource source = _serviceContainer.GetService<IFilesSourceBuilderService>(type).Create(this, name, path);

// Get the defined paths of the source
if (source != null)
{
string[] paths = configuration.GetPaths(key + "/DefinedPath", "Name");

foreach (string definedPath in paths)
{
name = configuration.ReadValue<string>(definedPath, "Alias", string.Empty);
path = configuration.ReadValue<string>(definedPath, "Path", string.Empty);

source.AddDefinedPath(name, path);
}

_sources.Add(source);
}
}
}

#endregion
}
}

 

Now the VFS constructor uses the IConfigurationService to read the configuration values (from a xml file) file and the IFilesSourceBuilderService to create files sources (based on the type).

As you can see Pluto give us the ability to decouple components (services as interfaces, quering services to the ServiceContainer instead of having lots of statics properties and methods in the code) while doing things in a more .NET way (heavy usage of attributes). In the next post I'll probably talk about other nice things that are inside Pluto and how they relate to the engine.

Comments

# re: Writing a plugin for Pluto

Monday, June 11, 2007 10:58 AM by Reed

Nice discussion of Pluto, Vicente. Great to see the VFS moved to a general use plugin, as well. Great job! -Reed

Leave a Comment

(required) 
(required) 
(optional)
(required) 

Enter the numbers above: