# Jenkins Extension Point System and Descriptors: How Plugin Architecture Works

> Understand the Jenkins extension point system and how Descriptors work. Explore plugin architecture with interfaces, abstract classes, and metadata factories.

- Repository: [Jenkins/jenkins](https://github.com/jenkinsci/jenkins)
- Tags: internals
- Published: 2026-06-19

---

**Jenkins extension points are Java interfaces or abstract classes that define contracts for plugins, while Descriptors are singleton metadata factories that handle UI configuration and instantiation of Describable objects, with both systems wired together via the `@Extension` annotation and `ExtensionList` registries.**

The Jenkins extension point system forms the backbone of its plugin architecture, enabling seamless integration of custom functionality into the CI/CD platform. Located in the `jenkinsci/jenkins` repository, this system uses the `@Extension` annotation and `ExtensionPoint` interface to automatically discover and register plugin components at runtime. Understanding how Descriptors work alongside these extension points is essential for developing plugins that require user configuration through the Jenkins web interface.

## What Is the Jenkins Extension Point System?

The Jenkins extension point system is built around the `ExtensionPoint` marker interface found in [`hudson/ExtensionPoint.java`](https://github.com/jenkinsci/jenkins/blob/main/hudson/ExtensionPoint.java). Extension points are defined as plain Java interfaces or abstract classes that declare a contract which plugins may implement.

When a plugin provides an implementation, it annotates the class with `@Extension` (defined in [`hudson/Extension.java`](https://github.com/jenkinsci/jenkins/blob/main/hudson/Extension.java)). Jenkins automatically discovers these annotated classes at startup or when a plugin loads, creates singleton instances, and registers them in an `ExtensionList` associated with that specific extension point.

The `ExtensionList` class (in [`hudson/ExtensionList.java`](https://github.com/jenkinsci/jenkins/blob/main/hudson/ExtensionList.java)) maintains these collections of discovered components. You can retrieve all implementations of a specific extension point using `ExtensionList.lookup(MyExtension.class)`, which returns the registry containing every registered instance.

## How Descriptors Work in Jenkins

### The Describable-Descriptor Relationship

Most extension points are implemented by `Describable` objects—components that require UI-based configuration. Every `Describable` has a corresponding `Descriptor` (a subtype of `hudson.model.Descriptor`) that acts as its metadata factory and controller.

This relationship mirrors Java's `Class` ↔ `Object` paradigm. The `Descriptor` holds a reference to the `Class<? extends T> clazz` it describes and maintains singleton status for the descriptor itself. In [`hudson/model/Descriptor.java`](https://github.com/jenkinsci/jenkins/blob/main/hudson/model/Descriptor.java), the base class is defined as:

```java
public abstract class Descriptor<T extends Describable<T>> implements Loadable, Saveable, OnMaster { … }

```

### Descriptor Responsibilities

The `Descriptor` class handles several critical functions:

- **Metadata**: `getDisplayName()` returns the human-readable label, while `getHelpFile()` provides documentation paths
- **Factory**: `newInstance(StaplerRequest2, JSONObject)` creates new configurable instances from submitted JSON form data
- **Persistence**: `load()` and `save()` handle XML serialization of global configuration
- **Data Binding**: `getPropertyType(...)` exposes property types to Jelly/Stapler for form generation

### DescriptorExtensionList and Registration

Descriptors themselves are registered as extensions. The nested `DescriptorImpl` class is annotated with `@Extension`, making it a singleton component. The `DescriptorExtensionList` class (in [`hudson/DescriptorExtensionList.java`](https://github.com/jenkinsci/jenkins/blob/main/hudson/DescriptorExtensionList.java)) maintains a list of all descriptors for a given extension point.

You can retrieve all descriptors for a specific type using `Jenkins.get().getDescriptorList(MyBuilder.class)`, which returns the `DescriptorExtensionList` containing every registered descriptor for that describable type.

## Extension Discovery Lifecycle

The discovery mechanism follows this sequence:

1. **Scanning**: Jenkins uses the SezPoz index to locate all classes, static fields, and methods annotated with `@Extension`
2. **Instantiation**: For each discovered element, Jenkins creates an instance or uses the provided static field value, adding it to the appropriate `ExtensionList`
3. **Descriptor Registration**: When the descriptor class (annotated with `@Extension`) is scanned, Jenkins creates the singleton descriptor and adds it to the `DescriptorExtensionList` for that extension point
4. **UI Binding**: When users configure the plugin, Stapler invokes the descriptor's `newInstance(...)` method to build concrete `Describable` instances from JSON data

## Practical Implementation Examples

The following examples demonstrate common patterns when working with the Jenkins extension point system and Descriptors.

### Creating a Custom Publisher with Descriptor

This example from `hudson/tasks/Publisher` shows a `Recorder` implementation with its nested `DescriptorImpl`:

```java
package org.example.jenkins;

import hudson.Extension;
import hudson.model.AbstractProject;
import hudson.tasks.BuildStepDescriptor;
import hudson.tasks.Publisher;
import hudson.tasks.Recorder;
import java.io.IOException;
import org.kohsuke.stapler.StaplerRequest;
import net.sf.json.JSONObject;

public class HelloWorldPublisher extends Recorder {

    private final String greeting;

    public HelloWorldPublisher(String greeting) {
        this.greeting = greeting;
    }

    @Override
    public boolean perform(AbstractProject<?,?> project, hudson.Build build, hudson.FilePath workspace,
                           hudson.Launcher launcher, hudson.TaskListener listener) throws IOException, InterruptedException {
        listener.getLogger().println("Hello from the publisher: " + greeting);
        return true;
    }

    public String getGreeting() {
        return greeting;
    }

    @Extension
    public static class DescriptorImpl extends BuildStepDescriptor<Publisher> {

        public DescriptorImpl() {
            load();
        }

        @Override
        public boolean isApplicable(Class<? extends AbstractProject> jobType) {
            return true;
        }

        @Override
        public String getDisplayName() {
            return "Say Hello";
        }

        @Override
        public Publisher newInstance(StaplerRequest req, JSONObject formData) throws FormException {
            return req.bindJSON(HelloWorldPublisher.class, formData);
        }

        @Override
        public boolean configure(StaplerRequest req, JSONObject json) throws FormException {
            save();
            return true;
        }
    }
}

```

### Retrieving Registered Descriptors

To access all descriptors for a specific extension point:

```java
import jenkins.model.Jenkins;
import org.example.jenkins.HelloWorldPublisher;

public class ListAllPublishers {
    public static void main(String[] args) {
        var list = Jenkins.get().getDescriptorList(HelloWorldPublisher.class);
        for (var d : list) {
            System.out.println("Descriptor ID: " + d.getId());
            System.out.println("Display name: " + d.getDisplayName());
        }
    }
}

```

This invokes the `DescriptorExtensionList` mechanism to enumerate all registered descriptors.

### Working with ExtensionList Directly

For simple extension points without UI configuration:

```java
import hudson.ExtensionList;
import hudson.Extension;
import hudson.ExtensionPoint;
import jenkins.model.Jenkins;

@Extension
public class MyTrigger implements ExtensionPoint {
    public void fire() {
        System.out.println("Triggered!");
    }
}

/* Somewhere else */
ExtensionList<MyTrigger> triggers = ExtensionList.lookup(MyTrigger.class);
for (MyTrigger t : triggers) {
    t.fire();
}

```

## Summary

- **ExtensionPoint** ([`hudson/ExtensionPoint.java`](https://github.com/jenkinsci/jenkins/blob/main/hudson/ExtensionPoint.java)) defines the contract that plugins implement
- **@Extension** annotation triggers automatic discovery and registration in `ExtensionList` instances
- **Descriptor** ([`hudson/model/Descriptor.java`](https://github.com/jenkinsci/jenkins/blob/main/hudson/model/Descriptor.java)) provides metadata, factory methods, and persistence for `Describable` objects
- **DescriptorExtensionList** ([`hudson/DescriptorExtensionList.java`](https://github.com/jenkinsci/jenkins/blob/main/hudson/DescriptorExtensionList.java)) manages singleton descriptor instances for each extension point
- The discovery flow uses SezPoz indexing at startup to wire components together without manual registration

## Frequently Asked Questions

### What is the difference between ExtensionPoint and Descriptor?

ExtensionPoint is a marker interface for contracts that plugins can implement, while Descriptor is a metadata class that describes how to create and configure instances of a Describable extension point. ExtensionPoints define the "what" (the capability), while Descriptors handle the "how" (instantiation, UI labels, and persistence).

### How does Jenkins discover plugin extensions at startup?

Jenkins uses the SezPoz annotation index to scan the classpath for all elements annotated with `@Extension` (defined in [`hudson/Extension.java`](https://github.com/jenkinsci/jenkins/blob/main/hudson/Extension.java)). It instantiates these classes or retrieves static field values, then registers them in the appropriate `ExtensionList` registry. This process occurs when Jenkins starts or when a plugin is dynamically loaded.

### When should I use DescriptorExtensionList vs ExtensionList?

Use `DescriptorExtensionList` when you need to access metadata about configurable extension points (such as listing all available build steps), while `ExtensionList` is used for accessing actual singleton instances of simple extension points. `DescriptorExtensionList` is specifically for `Descriptor` objects associated with `Describable` types.

### Why is the @Extension annotation required on Descriptor classes?

The `@Extension` annotation is required because `Descriptor` itself implements `ExtensionPoint`, making it part of the extension system. Without the annotation, Jenkins would not discover the descriptor during startup scanning, meaning the plugin would not appear in configuration dropdowns and its `newInstance()` factory method would never be called for UI configuration.