Hi Alex, all,
As promised, here's a list of ideas for the plugin subsystem, many of which we had with Nicolas when we brainstormed on the subject a while ago. Though most of the topics raised here will need to be addressed by the plugin subsystem one way or the other, everything is open for discussion and by no means set in stone. So feel free to challenge those ideas or suggest better solutions.
1) Plugin files
Plugins are Jar/Zip archives and contain class files and resources. It may be a good idea to use a specific file extension for muCommander plugins like .mpl, instead of just going for the obvious .jar extension. While I don't have a particular reason in mind, I think we may have a use for this down the road.
2) Plugin directory
Plugins could live inside a subdirectory of the preferences directory, e.g. ~/.mucommander/plugins/ . This directory would be scanned by muCommander when it starts up, and the plugins that are there would be added to the classpath dynamically. Note that we already have a custom classloader (AbstractFileClassLoader) that was designed with this purpose in mind.
3) Meta-information / Versioning
Just like the main application, plugins will have to be versioned. The version information could be stored in a MANIFEST.MF file inside the plugin's mpl/jar file (e.g. Plugin-Version: 1.0), along with some meta-information such as a displayable name and description (Plugin-Name, Plugin-Description). We'll also have to address dependencies towards the main application : a plugin built for a specific version of muCommander may or may not work with a more recent version of the application. If we know the app version that the plugin was built for, we can warn the user that the plugin may need to be upgraded and/or choose not to load the plugin. We could add a property to the manifest for that purpose, e.g. 'Built-For-Version: 0.9'.
It would also be nice for plugins to report when a newer version is available for download. We could extend the existing version-checking mechanism that exists in the main application, and allow plugins to specify a URL that points to the XML file that describes the latest plugin version. Again, this could be stored as a manifest property, e.g. Plugin-Version-Check-URL:
http://whatever.org/latest.xml.
4) Configuration
Plugins may want to let the user configure certain things. We should offer a standard way for plugins to store and retrieve configuration variables. The com.mucommander.conf API (the one used for the main preferences.xml file) lets multiples configuration instances be used, so we can reuse it and have one configuration instance per plugin. This will leave the main preferences.xml clean of plugin configuration values, and limit chances of conf variable collision.
The plugin configuration file should be located somewhere stable, for instance in a '<plugin-name>_config.xml' file located in the same directory as the plugin's mpl/jar file.
A Configuration instance would be given to the plugin on initialization and would be saved when the application quits.
Alternatively, the configuration could be stored inside the plugin's mpl/jar file, for instance as a 'config.xml' at the root of the archive, but in that case, special care should be taken to ensure that the configuration does not get lost when the plugin is updated. That means plugins would need to be updated by muCommander, and not manually by overwriting the plugin file or else the configuration would be lost, which may not be ideal.
5) Localization
Plugins that offer GUI should be localized. We should offer a standard way for plugins to do so. The com.mucommander.text.Translator class and dictionary file format (the one used for the localization of the main application) could be used for that. Plugins could have a 'dictionary.txt' entry at the root of the archive that contains the localized entries. A corresponding Translator instance would be automatically be created and passed to the plugin on startup.
Note that the Translator class would have to be modified to let multiple Translator instances exist -- currently it's all static.
6) Draft interface
To sum it all up, below is a proposal for a simple plugin interface (Plugin) and supporting classes (PluginContext and PlugInfo).
What do you think ?
Cheers,
Maxence
------------------------------------------------------------
// The interface to be implemented by plugins
public interface Plugin {
// This is where the plugin initializes itself: register actions, filesystems, ...
public void init(PluginContext context);
// This is where the plugin shuts itself down to leave the application as it was before: unregister actions, filesystem, ...
public void deinit(PluginContext context);
}
// Exposes the different amenities provided to plugins
public interface PluginContext {
// Allows the plugin to be configured.
public Configuration getConfiguration();
// Provides access to the plugin's dictionary.
public Translator getTranslator();
// Provides access to the meta-information contained by the plugin's manifest.
public PluginInfo getPluginInfo();
// Returns the plugin file, some plugins may have a use for it.
public AbstractFile getPluginFile();
// Returns the current muCommander version.
public String getApplicationVersion();
}
// Exposes the meta-information contained by a plugin's MANIFEST.MF file.
public interface PluginInfo {
// Plugin name, e.g. "Directory synchronizer".
public String getName();
// Plugin description, e.g. "Synchronizes two directories, copying files that are not present in both directories".
public String getDescription();
// Plugin author, e.g. "John Doe".
public String getAuthor();
// Plugin version, e.g. "1.0".
public String getVersion();
// muCommander version the plugin was built for, e.g. "0.9".
public String getBuiltForVersion();
public FileURL getVersionCheckURL();
}