Notes on a general-purpose plug-in / extension mechanism

Author: Jeff Dalton

Updated: Tue Jan 17 14:41:59 2006 by Jeff Dalton


1. Rationale

I-X agents typically have a number of points where objects of
certain types can be "plugged-in".  For example, it's possible
to add a new tool to an I-P2's "Tools" menu, or to add a handler.

It's easy to do this in Java code, but you may have to
define an Ip2 subclass that creates and plugs-in the desired
objects at appropriate points.  That can be awkward when
you want to be able to use several different combinations
of plug-ins; and when when two or more people are making
changes by subclassing, it takes work to combine them.

In some cases, there is a more flexible mechanism.  For
instance, if you want to add issue or activity handlers,
there's a command-line argument, "additional-handlers",
that lets you specify them by giving a list of class names.

To avoid a proliferation of such special-purpose mechanisms,
and to cover cases where no mechanism already exists, as well
as unanticipated cases that aren't supported as plug-ins,
a more general-purpose mechanism is provided.  In effect,
it lets you specify Java code that is run when an agent
starts up.  It can create objects and plug them in, or it
can do something else.  We use "extension" as a convenient
term for all such additions to an agent.


2. Using agent extensions

There's a command-line argument / props file entry called
"extension-classes".  The value should be a comma-separated list
of fully-qualified class names.  (You also have to make sure
the CLASSPATH includes those classes.)


3. Writing agent extensions

Each such class must implement the ix.icore.IXAgentExtension
interface and must have a public 1-argument constructor that
declares its parameter as IXAgent or an IXAgent subclass
(such as Ip2 or IPlan).

When an I-P2 starts up, it makes one instance of
each of the specified classes and calls the instance's
installExtension() method.  The classes are processed
in the order specified by the "extension-classes" parameter.

The class definition will look something like this:

    import ix.icore.IXAgentExtension;
    import ix.ip2.Ip2;

    public class SomeIp2Extension implements IXAgentExtension {

        private Ip2 agent;  // Ip2 is a subclass of IXAgent

        public SomeIp2Extension(Ip2 agent) {
            this.agent = agent;
        }

        public void installExtension() {
            ...
        }
    }

An installExtension method that added a tool would look
something like this:

    import ix.iface.util.ToolController;

    public void installExtension() {
        agent.addTool(new ToolController("Menu Text") {
            public Object createTool() {
                return new ToolClass(ip2);
            }
        });
    }

The exact point at which all of this happens may need some
adjusting.  Right now it happens after all of startup /
initialisation except for initialising options and loading
the initial plan.

It's possible that a future version will have several methods
that are invoked at different points.

