New Plugin Manager
Revised: 31 August, 2005 LarryStone
This Plugin Manager has been substantially redesigned to be more
useful throughout the platform, and on a larger scale, than the first
proposal. Its feature set has been reduced to just what is needed
at the present time.
PluginManager is a very simple component container.
It creates and organizes components (plugins), and helps select a plugin
in the cases where there are many possible choices. It also gives
some limited control over the lifecycle of a plugin.
Please keep in mind, it is not an "inversion of control" (IoC) framework like
DSpace's `PluginManager` is what IoC folks call an "active dependency
manager". It could be used as the engine behind a simple IoC
framework, if that is desired someday.
See ModularityMechanism for more details about coding techniques and
standards to facilitate moving to a plug-in architecture.
This proposal is only concerned with the machinery of choosing a
plugin from among various implementations.
Also see EvolutionToServiceLocatorProposal for a picture of a
DSpace platform where all modules are plugins. It can be
built on the "singleton"
plugin mechanism from this proposal.
Be familiar with the following terms before reading the rest of this document:
- Plugin Interface
A Java interface, the defining characteristic of a plugin. The consumer of a plugin asks for its plugin by interface.
a.k.a. Component, this is an instance of a class that implements a certain interface. It is interchangeable with other implementations, so that any of them may be "plugged in", hence the name. A Plugin is an instance of any class that implements the plugin interface.
- Implementation class
The actual class of a plugin. It may implement several plugin interfaces, but must implement at least one.
Plugin implementations can be distinguished from each other by name, a short
Stringmeant to symbolically represent the implementation class. They are called "named plugins". Plugins only need to be named when the caller has to make an active choice between them.
- SelfNamedPlugin class
Plugins that extend the `SelfNamedPlugin` class can take advantage of additional features of the Plugin Manager. Any class can be managed as a plugin, so it is not necessary, just possible.
Reusable plugins are only instantiated once, and the Plugin Manager returns the same (cached) instance whenever that same plugin is requested again. This behavior can be turned off if desired.
Using the Plugin Manager
Types of Plugin
The Plugin Manager supports three different patterns of usage:
1. Singleton Plugins
There is only one implementation class for the plugin. It is
indicated in the configuration.
For examples, see
This type of plugin chooses an implementation of a service, for
the entire system, at configuration time.
Your application just fetches the
plugin for that interface and gets the configured-in choice.
See the #PluginManager Class getSinglePlugin() method.
2. Sequence Plugins
You need a sequence or series of plugins, to implement a mechanism
like StackableAuthenticationMethods or a pipeline, where each plugin
is called in order to contribute its implementation of a process to the whole.
The Plugin Manager supports this by letting you configure a sequence
of plugins for a given interface. See the
#PluginManager Class getPluginSequence() method for details.
3. Named Plugins
Use a named pluginwhen the application has to choose one plugin implementation out of
many available ones.
is bound to one or more names (symbolic identifiers) in the
The name is just a `String` to be associated with the combination of
implementation class and interface. It may contain any characters
except for comma (`,`) and equals (`=`). It may contain embedded spaces. Comma is
a special character used to separate names in the configuration entry.
Names must be unique within an interface: No plugin classes
implementing the same interface may have the same name.
Think of plugin names as a controlled vocabulary – for a given
plugin interface, there is a set of names for which plugins can be
found. The designer of a Named Plugin interface is responsible for
deciding what the name means and how to derive it; for example, names
of metadata crosswalk plugins may describe the target metadata format.
See the #PluginManager Class getNamedPlugin() method and the
#PluginManager Class getPluginNames() method
for more details.
Named plugins can get their names either from the
configuration or, for a variant called self-named plugins, from
within the plugin itself.
Self-named plugins are necessary because one plugin implementation can
be configured itself to take on many "personalities", each of which
deserves its own plugin name. It is already managing its own
configuration for each of these personalities, so it makes sense to
allow it to export them to the Plugin Manager rather than expecting
the plugin configuration to be kept in sync with it own configuration.
An example helps clarify the point: There is a named plugin that does
crosswalks, call it CrosswalkPlugin. It has several implementations
that crosswalk some kind of metadata. Now we add a new plugin
which uses XSL stylesheet transformation (XSLT) to crosswalk many
types of metadata – so the single plugin can act like many different
plugins, depending on which stylesheet it employs.
This XSLT-crosswalk plugin has its own configuration that maps
a Plugin Name to a stylesheet – it has to, since of course the Plugin
Manager doesn't know anything about stylesheets. It becomes a self-named
plugin, so that it reads its configuration data, gets the list of names
to which it can respond, and passes those on to the Plugin Manager.
When the Plugin Manager creates an instance of the XSLT-crosswalk,
it records the Plugin Name that was responsible for that instance.
The plugin can look at that Name later in order to configure itself
correctly for the Name that created it. This mechanism is all part
of the `SelfNamedPlugin` class which is part of any self-named plugin.
Obtaining a Plugin Instance
The most common thing you will do with the Plugin Manager is obtain an
instance of a plugin. To request a plugin, you must always specify
the plugin interface you want. You will also supply a name when
asking for a named plugin.
When `PluginManager` fulfills a request for a plugin, it checks whether
the implementation class is reusable; if so, it creates one
instance of that class and returns it for every subsequent request for
that interface and name. If it is not reusable, a new instance is always
For reasons that will become clear later, the manager actually caches
a separate instance of an implementation class for each name under
which it can be requested.
You can ask the `PluginManager` to forget about (decache) a
plugin instance, by releasing it. See the `PluginManager.releasePlugin()`
method. The manager will drop its reference to the plugin so the garbage
collector can reclaim it. The next time that plugin/name combination is
reuqested, it will create a new instance.
The `PluginManager` can list all the names of the
Named Plugins which implement an interface.
You may need this, for example, to implement a menu in a user interface
that presents a choice among all possible plugins.
See the method
#PluginManager Class getPluginNames().
Note that it only returns the plugin name, so if you need a
more sophisticated or meaningful "label" (i.e. a key into the I18N
message catalog) then you should add a method to the plugin itself to
Note: The `PluginManager` refers to interfaces and classes internally
only by their names whenever possible, to avoid loading classes until
absolutely necessary (i.e. to create an instance). As you'll see
below, self-named classes still have to be loaded to query them for names,
but for the most part it can avoid loading classes. This saves a lot
of time at start-up and keeps the JVM memory footprint down, too. As
the Plugin Manager gets used for more classes, this will become a greater
The only downside of "on-demand" loading is that errors in the configuration
don't get discovered right away. The solution is to call the
`checkConfiguration()` method after making any changes to the configuration.
The `PluginManager` class is your main interface to the Plugin Manager.
It behaves like a factory class that never gets instantiated, so
its public methods are `static`.
Here are the public methods, followed by explanations:
Returns an instance of the singleton (single) plugin implementing
the given interface. '''There must be exactly one single plugin
configured for this interface,''' otherwise the `PluginConfigurationError`
Note that this is the only "get plugin" method which throws an exception.
It is typically used at initialization time to set up a
permanent part of the system so any failure is fatal.
See the `plugin.single` configuration key for configuration details.
Returns instances of all plugins that implement
the interface intface, in an `Array`. Returns an empty array
if no there are no matching plugins.
The order of the plugins in the array is the same as their class names
in the configuration's value field.
See the `plugin.sequence` configuration key for configuration details.
Returns an instance of a plugin that implements the interface intfaceand is bound to a name matching name. If there is no matching
plugin, it returns `null`. The names are matched by `String.equals()`.
See the `plugin.named` and
`plugin.selfnamed` configuration keys for configuration details.
Tells the Plugin Manager to let go of any references to a reusable
plugin, to prevent it from being given out again and to allow the
object to be garbage-collected. Call this when a plugin instance
must be taken out of circulation.
Returns all of the names under which a named plugin implementing
the interface intface can be requested (with `getNamedPlugin()`).
The array is empty if there are no matches. Use this to populate a
menu of plugins for interactive selection, or to document what the
possible choices are.
''The names are NOT returned in any predictable order, so you may wish
to sort them first.''
Note: Since a plugin may be bound to more than one name, the
list of names this returns does not represent the list of plugins.
To get the list of unique implementation classes corresponding to the
names, you might have to eliminate duplicates (i.e. create a `Set` of classes).
Validates the keys in the DSpace `ConfigurationManager` pertaining
to the Plugin Manager and reports any errors by logging them.
This is intended to be used interactively by a DSpace administrator,
to check the configuration file after modifying it.
See the section about #Validating the Configuration for details.
A named plugin implementation must `extend` this class if it
wants to supply its own Plugin Name(s). See
Self-Named Plugins for why this is sometimes necessary.
Errors and Exceptions
An error of this type means the caller asked for a single plugin, but
either there was no single plugin configured matching that interface,
or there was more than one. Either case causes a fatal configuration error.
This exception indicates a fatal error when instantiating a plugin class.
It should only be thrown when something unexpected happens in the
course of instantiating a plugin, e.g. an access error, class not found,
etc. Simply not finding a class in the configuration is not an exception.
This is a `RuntimeException` so it doesn't have to be declared, and can
be passed all the way up to a generalized fatal exception handler.
All of the Plugin Manager's configuration comes from the DSpace
Configuration Manager, which is a Java `Properties` map. You can
configure these characteristics of each plugin:
#Interface: Classname of the Java interface which defines the plugin, including package name. e.g. `org.dspace.app.mediafilter.MediaFilter`
#Implementation Class: Classname of the implementation class, including package. e.g. `org.dspace.app.mediafilter.PDFFilter`
#Names: (Named plugins only) There are two ways to bind names to plugins: listing them in the value of a `plugin.named.`interface key, or configuring a class in `plugin.selfnamed.`interface which extends the `SelfNamedPlugin` class.
#Reusable option: (Optional) This is declared in a `plugin.reusable` configuration line. Plugins are reusable by default, so you only need to configure the non-reusable ones.
Configuring Singleton (Single) Plugins
This entry configures a Single Plugin for use with `getSinglePlugin()`:
For example, this configures the class `org.dspace.app.webui.SimpleAuthenticator`
as the plugin for interface `org.dspace.app.webui.SiteAuthenticator`:
Configuring Sequence of Plugins
This kind of configuration entry defines a Sequence Plugin,
which is bound to a
sequence of implementation classes. The key identifies the
interface, and the value is a comma-separated list of classnames:
The plugins are returned by `getPluginSequence()` in the same order as
their classes are listed in the configuration value.
For example, this entry configures the StackableAuthenticationMethods
with three implementation classes:
Configuring Named Plugins
There are two ways of configuring named plugins:
1. Plugins Named in the Configuration
A named plugin which gets its name(s) from the configuration is listed
in this kind of entry:
The syntax of the configuration value is: classname, followed by
an equal-sign and then at least one plugin name. ind more names
to the same implementation class by by adding them here, separated by commas.
Names may include any character other than comma
For example, this entry creates one plugin with the names `GIF`, `JPEG`,
and `image/png`, and another with the name `TeX`:
This example shows a plugin name with an
embedded whitespace character. Since comma (`,`) is the separator
character between plugin names, spaces are legal (between words of
a name; leading and trailing spaces are ignored).
This plugin is bound to the names "`Adobe PDF`", "`PDF`", and
"`Portable Document Format`".
NOTE: Since there can only be one key with `plugin.named.` followed
by the interface name in the configuration, all of the plugin
implementations must be configured in that entry.
2. Self-Named Plugins
Since a self-named plugin supplies its own names through a static
method call, the configuration only has to include its interface and
The following example
first demonstrates how the plugin class,
`XsltDisseminationCrosswalk` is configured to implement
its own names "`MODS`" and "`DublinCore`". These come from
the keys starting with `crosswalk.dissemination.stylesheet.`. The
value is a stylesheet file.
The class is then configured as a self-named plugin:
NOTE: Since there can only be one key with `plugin.selfnamed.` followed
by the interface name in the configuration, all of the plugin
implementations must be configured in that entry. The
`MODSDisseminationCrosswalk` class is only shown to illustrate this point.
Configuring the Reusable Status of a Plugin
Plugins are assumed to be reusable by default, so you only need
to configure the ones which you would prefer not to be reusable.
The format is as follows:
For example, this marks the PDF plugin from the example above as non-reusable:
Validating the Configuration
Anchor(validate)The Plugin Manager is very sensitive to mistakes in
the DSpace configuration. Subtle errors can have unexpected consequnces
that are hard to detect: for example, if there are two
`"plugin.single"` entries for the same interface, one of them will be
To validate the Plugin Manager configuration, call the
`PluginManager.checkConfiguration()` method. It looks for the following
- Any duplicate keys starting with `"plugin."`.
- Keys starting `plugin.single`, `plugin.sequence`, `plugin.named`, and `plugin.selfnamed` that don't include a valid interface.
- Classnames in the configuration values that don't exist, or don't implement the plugin interface in the key.
- Classes declared in `plugin.selfnamed` lines that don't extend the `SelfNamedPlugin` class.
- Any name collisions among named plugins for a given interface.
- Named plugin configuration entries without any names.
- Classnames mentioned in `plugin.reusable` keys must exist and have been configured as a plugin implementation class.
The `PluginManager` class also has a `main()` method which simply runs
`checkConfiguration()`, so you can invoke it from the command line to
test the validity of plugin configuration changes.
Eventually, someone should develop
a general configuration-file sanity checker for DSpace, which would
just call `PluginManager.checkConfiguration()`.
Here are some usage examples to illustrate how the Plugin Manager works.
Managing the MediaFilter plugins transparently
The existing DSpace 1.3 `MediaFilterManager` implementation can
be largely replaced by
the Plugin Manager. The `MediaFilter` classes
become plugins named in the configuration.
These lines are added to the DSpace configuration:
Add this code to `MediaFilterManager` where it chooses a plugin. Note
how the plugin name comes from the `itstreamFormat`'s short name; this
is part of the design and contract of that particular plugin.
A Singleton Plugin
This shows how to configure and access a single anonymous plugin,
such as the `SiteAuthenticator` plugin in DSpace 1.3:
The following code fragment shows how `siteAuth`, the service object, is
initialized and used:
Plugin that Names Itself
This crosswalk plugin acts like many different plugins since it is
configured with different XSL translation stylesheets. Since it already
gets each of its stylesheets out of the DSpace configuration, it makes sense
to have the plugin give `PluginManager` the names to which it answers
instead of forcing someone to configure those names in two places
(and try to keep them synchronized).
NOTE: Remember how `getPlugin()` caches a separate instance of
an implementation class for every name bound to it? This is why:
the instance can look at the name under which it was invoked and configure
itself specifically for that name. Since the instance for each name
might be different, the Plugin Manager has to cache a separate
instance for each name.
Here is the configuration file listing both the plugin's own configuration
and the `PluginManager` config line:
This look into the implementation shows how it finds configuration
entries to populate the array of plugin names returned by the
`getPluginNames()` method. Also note, in the `getStylesheet()` method,
how it uses the plugin name that created the current instance (returned
by `getPluginInstanceName()`) to find the correct stylesheet.
The Stackable Authentication mechanism needs
to know all of the plugins configured for the interface, in the
order of configuration,
since order is significant. It gets a Sequence Plugin from the
Configuring stackable authentication would look like this:
Within the `AuthenticationManager`, the code to initialize the "stack"
of plugins looks like this:
- There may be a need someday for "named sequence of plugins": For example, if an application can choose between different "pipelines" of classes, it would want the Plugin Manager to offer alternative sequences of plugins for the same interface. To put it another way, Named sequences are to sequence plugins as Named Plugins are to Single Plugins. This can be left for future implementation, once there is a demonstrated need.