Plugin framework

In JNode, all code, services and resources are packaged into plugins.

Each plugin has a descriptor that defines the packages it contains, the plugins it depends on, and any extensions. The plugin-descriptors are held in the descriptors/ directory of each subproject. During the build, once the subprojects have been compiled, the plugins are assembled based on the descriptors that are found.

Plugins are collectively packaged into an initjar. This jar file is passed on the command line to grub when booting JNode and defines what is available to JNode during boot (drivers and such), as well after boot (commands/applications).

-- JNode Plugins --

A JNode plugin is defined by an xml file, its descriptor, contained in the descriptors/ directory of the subproject it belongs too. Filesystem plugins are in fs/descriptors, shell plugins in shell/descriptors and so on.

The root node of a plugin descriptor is >plugin< which takes a few required arguments that give the id, name, version and license.

id : the plugin id. This is the name that other plugins will use for dependencies, and the plugin-list will 
     use to include the plugin in an initjar.
name : A short descriptive name of what the plugin is.
version : The version of the plugin. For non-jnode plugins, this should be the version of the software being
          included. For JNode plugins, use @VERSION@.
license-name : the name of the license the code in the plugin defines. JNode uses lgpl
provider-name : The name of the project that provided the code, JNode.org for jnode plugins.
class(optional) : If the plugin requires special handling when loading/unloading the plugin, it can define a
                  class here that extends org.jnode.plugin.Plugin, overriding the start() and stop() methods.

Under the <plugin> node are definitions for different parts of the plugin. Here you define what the plugin includes, what it depends on, and any extensions.

The <runtime> node defines what a plugin is to include in its jar-file.

<runtime>
  <library> name="foo.jar>
    <export name="foo.*">
  </library>
</runtime>

This will export the classes that match foo.* in foo.jar to a jar file. This is how you would include classes from a non-jnode library into a plugin for use in jnode. To have a plugin include jnode-specific classes, the library name is of the form "jnode-.jar" and tells the plugin builder not to look in a jar file, but to pull the classes from the build/ directory of that jnode subproject.

To declare dependencies for a plugin, a list of <import> nodes under a <requires> node is required.

<requires>
  <import plugin="org.jnode.shell"/>
</requires>

Will add a dependency to the org.jnode.shell plugin for this plugin. The dependency does two things. When a plugin is included in a plugin-list, its dependencies must also be included, or the initjar builder will fail.

Each plugin has its own classloader. If commands or applications defined in a plugin are run, instead of using a classpath to find classes and jars, the plugin uses the dependencies to search for the proper classes. Every plugin class loader has access to the system plugins, its own plugin, and any plugins listed as dependencies. This means that no plugin needs to require a system plugin.

The last part of a plugin are the extensions. These are not specific to plugins, but rather to different parts of jnode that use the plugin. An extension is defined as :

<extension point="some.extension.point">

The content of an extension is defined by its point. Below is a brief list of extension points and where to find documentation on them.

Shell Extensions
point="org.jnode.shell.aliases"
    Used to define aliases for the alias manager in the shell.

point="org.jnode.shell.syntaxes"
    Used to define a syntax for command line arguments to an alias.

Core Extensions
point="org.jnode.security.permissions"
    Used to define a syntax for command line arguments to an alias.

Core Extensions
point="org.jnode.security.permissions"
    Used to define what permissions the plugin is granted.

-- Plugin List --

A plugin list is used to build an initjar and includes all the plugin jars that are defined in its list. The default plugin lists are in all/conf and these lists are read, and their initjars built by default. To change this behavior there are two options in jnode.properties that can be added to tell the build system where to look for custom plugin-lists, and also to turn off building the default plugins.

jnode.properties

custom.plugin-list.dir = 
    Directory can be any directory. ${root.dir} can be used to prefix the path with the directory of your jnode build.
no.default.initjars = 1
    Set to 1 to disable building the default initjars

A plugin list has a very simple definition. The root node is <plugin-list> that takes a single name attribute that will be the name of the actual initjar. The list of plugins are defined by adding <plugin id="some.plugin"> entries. If a plugin is included that has dependencies, and those plugins are not in the list, the initjar builder will fail.

You can add entries into the initjar manifest file by adding a <manifest> node with a list of <attribute> nodes. Attributes have two arguments, key and value. At a minimum you will want the following manifest entries :

<manifest>
  <attribute key="Main-Class" value="org.jnode.shell.CommandShell"/>
  <attribute key="Main-Class-Arg" value="boot"/>
</manifest>

This tells jnode, when it finishes initializing, and loads the initjar, that it should run CommandShell.main() with a single argument "boot", so that it knows that this shell is the root shell.

There are many reasons to create your own initjar plugin-list. The most basic reason would be to reduce the overhead of building jnode. By turning off building the default initjars, and defining your own plugin-list for a custom initjar, you can reduce the rebuild time of jnode when making simple changes. It can also allow you to create new plugins and define them in a plugin-list without disturbing the default initjar plugin-lists.

For a basic starting point, the shell-plugin-list.xml creates an initjar that has the minimal plugins for loading jnode and starting a CommandShell. From there you can add plugins that you want, to add various features.

How to add a plugin to JNode

This page will describe how to add a java program to JNode as plugin, so that it can be called via its alias.

First of all you need to set up Eclipse (or your favorit IDE) as described in the readme, so that JNode builds without errors and you can use it (e.g. use JNode in VMWare).

There are different ways of extending JNode with a plugin.
A plugin can contain a class that extends Plugin and (or) normal java programs.
Every plugin is described by a descriptor.

For our example we will develop a plugin that contains a normal java program.

We need a name for our plugin : we will use sample, wich is also the packagename of our plugin.
It belongs to one of the JNodes subprojects in our case we will use the ordername sample in the shell subproject.

Every java-file for our plugin has to be in (or in subfolders):



\shell\src\shell\org\jnode\shell\sample

(for me it is d:\jnode\shell\src\shell\org\jnode\shell\sample)



Now we will write a small HelloWorld.java wich will be one of our plugin programs.
Here is the source of the file HelloWorld.java :

package org.jnode.shell.sample;

public class HelloWorld{

        public static void main(String[] args){

            System.out.println(“HelloWorld – trickkiste@gmx.de“);

        }

}

thats ok, but it will not be build until we create a descriptor and add our plugin to the JNode full-plugin-list.xml.



The plugin descriptor (org.jnode.shell.sample.xml stored in the descriptors folder of the shell subproject) and looks like this :

<?xml version="1.0" encoding="UTF-8"? >

<!DOCTYPE plugin SYSTEM "jnode.dtd">


<plugin id="org.jnode.shell.sample"

            name="Sample Plugin"

            version="0.2"

            license-name="lgpl"

            provider-name="Trickkiste">

<requires>

            <import plugin="org.jnode.shell"/>

</requires>

<runtime>

            <library name="jnode-shell.jar">

                        <export name="org.jnode.shell.sample.*"/>

            </library>

</runtime>

<extension point="org.jnode.shell.aliases">

            <alias name="HelloWorld" class="org.jnode.shell.sample.HelloWorld"/>

</extension>

</plugin>

Now we need to add our Plugin to the JNode full-plugin-list.xml, this file is located in jnode\all\conf your entry should look like this :

[...]

            <plugin id="org.jnode.util"/>

            <plugin id="org.jnode.vm"/>

            <plugin id="org.jnode.vm.core"/>

            <plugin id="org.jnode.shell.sample"/>

</plugin-list>

thats it, you can now build JNode and test your HelloWorld plugin by typing HelloWorld.

What we can do now is add „normal“ programs to JNode via its provided Pluginstructure.