Plugin infrastructure

Definition

A plugin provides a set of non visual services and graphical elements that refine the interface. A description of a plugin contribution is necessary to activate it if needed.

Plugin descriptor and plugin activation

The eval-plugin-descriptor project encapsulates the abstract definition of the plugin. Modello is used to generate from a xml description the Java classes that represent the plugin ndescriptor.

It has been decided to not use a service-based infrastructure (such as OSGI), so we need to control explicitly plugin loading, interdependencies and execution isolation. For that purpose we use ClassWorld which allows a fine grain control over classloaders.

Plugins are stored as jar files in a well known directory (specified through the plugins.dir system property). Jar files are retrieved and descriptors are extracted and parsed during the first access to the Plugin Manager.

A plugin classpath is restricted to:

  • Core D2 classes
  • Plugin classes (located at the root of the jar file)
  • Classes defined in jar files located under the plugin's lib folder
  • Classes exposed by plugins upon which current plugin depends

If a plugin depends on a non-existent plugin, or a plugin which can't be activated, then it won't be activated either.

Plugin Access

The GlobalPluginManager provides a unique access point to plugins present at application startup. However this class only is not enough since all plugins are necessarily active for a given project. The ProjectPluginManager access gives access to only the plugins registered for a given project. Instances of GlobalPluginManager and ProjectPluginManager are retrieved through the PluginManagerRegistry.

At project creation time user can register additional plugins with the project. Plugins marked as "core" will be automatically registered: user will be unable to unregister them. This is the case, for instance, for the d2-base-plugin plugin which provides core functionnalities.

Plugin description

Specifying a dependency

A plugin A declares a dependency on plugin B through the requires section (see Listing 1, below). The element require/id represents the id of the plugin upon which the current depends. Note that Package Names declaration is required.

<plugin>
    <id>org.decisiondeck.WeightedSum</id>
    <version>1.0</version>
    <className>org.decisiondeck.plugin.DefaultPlugin</className>
        <requires>
            <require>
                <id>org.decisiondeck.Base</id>
                <packageNames>
                    <packageName>org.decisiondeck.view.base</packageName>
                </packageNames>
            </require>
        </requires>
        ...
</plugin>

Listing 1

Exposed services

Plugins declare the services they expose with the services element (see Listing 2, below). This allow them to be then searchable by service extension id (org.decisiondeck.NavigationMenuItem in the example below).

<plugin>
    ...
        <services>
                <service>
                <extend>org.decisiondeck.NavigationMenuItem</extend>
                <id>WeightedSum Menu Item</id>
                <className>org.decisiondeck.view.weightedsum.WeightedSumMenuItem</className>
                <roles>
                    <role>coordinator</role>
                    <role>decision maker</role>
                </roles>
            </service>
        </services>
    ...
</plugins>

Listing 2

Database schema refinements

Plugins may need to refine the database schema. For instance a particular method may work only if criteria define a vector of weights. For that purpose plugins can refine database schema declaratively using the refines element (see Listing 3 below). In addition table joins are also to be declared using the joins element.

In addition there exist a specific element criteria whose text value references a table addition against which joins will be performed when querying criteria.

If a plugin is enabled for a specific project, tables will be created at plugin installation time (i.e. at project creation or later if plugin is late bound). To avoid name collisions,$ All created table will be prefixed with the plugin identifier. In the example below, a table named org_decisiondeck_WeightedSum_Parameters will be created.

<plugin>
    <id>org.decisiondeck.WeigthedSum</id>
    ...
    <refines>
        <tableAdditions>
            <tableAddition>
                <table>Parameters</table>
                <columns>
                    <column>
                        <name>OID</name>
                        <type>INT(5)</type>
                        <javaType>java.lang.Integer</javaType>
                        <hidden>true</hidden>
                        <primaryKey>true</primaryKey>
                    </column>
                    <column>
                        <name>Weight</name>
                        <type>FLOAT UNSIGNED</type>
                        <display>Weight</display>
                        <javaType>java.lang.Float</javaType>
                        <primaryKey>false</primaryKey>
                    </column>
                    <column>
                        <name>Criteria_ID</name>
                        <type>INT(5)</type>
                        <javaType>java.lang.Integer</javaType>
                        <hidden>true</hidden>
                        <primaryKey>false</primaryKey>
                        <references>Criteria</references>
                        <unique>true</unique>
                    </column>
                </columns>
            </tableAddition>
        </tableAdditions>
        <joins>
            <join>
                <source>Criteria</source>
                <destination>Parameters</destination>
                <segments>
                    <segment>
                        <source>OID</source>
                        <destination>Criteria_ID</destination>
                    </segment>
                </segments>
            </join>
        </joins>
        <criteria>Parameter</criteria>
    </refines>
    ...
</plugin>

Listing 3

Native Libraries

If a bundled library depends on native libraries, plugin dshould describe those native libraries thanks to the nativeDescriptors element. NativeDescriptors is a sequence (1-*) of NativeDescriptor. NativeDescriptor describes:

  • OS family
  • OS architecture
  • OS name
  • OS version
  • Folder that contains the native libraries matching this OS

Note that it is generally sufficient to only declare the OS family (e.g. windows, unix, ...). The listing 4 below is an example of how to use NativeDescriptors element (GLPK plugin).

<plugin>
    <id>org.decisiondeck.Glpk</id>
    <version>1.0</version>
    <hidden>true</hidden>
    <className>org.decisiondeck.plugin.DefaultPlugin</className>
    <nativeDescriptors>
        <nativeDescriptor>
                <osFamily>windows</osFamily>
                <folder>native/windows</folder>
        </nativeDescriptor>
        <nativeDescriptor>
                <osFamily>unix</osFamily>
                <folder>native/unix</folder>
        </nativeDescriptor>
    </nativeDescriptors>
</plugin>

Listing 4

Examples

Please see d2-weightedsum-plugin for a complete working example.