Defining a new extension point

From Eclipsepedia

Revision as of 21:40, 16 July 2013 by Scott.fisher.oracle.com (Talk | contribs)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

Just as Hudson core defines a set of extension points, your plugin can define new extension points so that other plugins can contribute implementations. This page shows how to do that.

First, you define your contract in terms of an abstract class (you can do this in interfaces, too, but then you'll have hard time adding new methods in later versions of your plugin.) This class should implement the marker hudson.ExtensionPoint interface, to designate that it is an extension point.

You also need to define the static "all" method (the method name doesn't really matter, but that's the convention.) The returned ExtensionList allows you discover all the implementations at runtime.

The following code shows this in a concrete example:

/**
 * Extension point that defines different kinds of animals
 */
public abstract class Animal implements ExtensionPoint {
    ...

    /**
     * All registered {@link Animal}s.
     */
    public static ExtensionList<Animal> all() {
        return Hudson.getInstance().getExtensionList(Animal.class);
    }
}

In addition to these basic ingredients, your extension point can implement additional interfaces, extend from another base class, define all sorts of methods, fields, constructors, and etc.

Also, this convention is only necessary when you need to discover the implementations at runtime. So if your extension point consists of a group of several classes and interfaces, normally only the top-level one needs to follow this convention (such an example can be seen in hudson.scm.ChangeLogParser in the core — ChangeLogParser implementations are always created by hudson.scmSCM implementations, so ChangeLogParser doesn't have the all method while SCM has it.

Enumerating implementations at runtime

The following code shows how you can list up all the Animal implementations contributed by other plugins.

for (Animal a : Animal.all()) {
    System.out.println(a);
}

There are other convenience methods to find a particular instance, and so on. See hudson.ExtensionList for more details.

Implementing extension points

Implementing an extension point defined in a plugin is no different from implementing an extension point defined in the core. See hudson.Extension for more details.

@Extension
public class Lion extends Animal { ... }

Doing Describable/Descriptor pattern

If you are going to define a new extension point that follows the hudson.model.Describable/hudson.model.Descriptor pattern, the convention is bit different.

You need to do this (instead of the simple extension point like Animal above) if:

  • you want to leverage the form handling
  • you need to define class-level operations, not just instance-level operations

For this, first you define your Describable subtype and Descriptor subtype. The all method is implemented slightly differently, and you provide the default getDescriptor implementation.

public abstract class Food implements Describable<Food>, ExtensionPoint {
    ...

    public FoodDescriptor getDescriptor() {
        return (FoodDescriptor)Hudson.getInstance().getDescriptor(getClass());
    }

    public static DescriptorExtensionList<Food,FoodDescriptor> all() {
        return Hudson.getInstance().<Animal,AnimalDescriptor>getDescriptorList(Food.class);
    }
}

public abstract class FoodDescriptor extends Descriptor<Food> {
    // define additional constructor parameters if you want
    protected FoodDescriptor(Class<? extends Food> clazz) {
        super(clazz);
    }
    protected FoodDescriptor() {
    }

    ...
}