# How to Modify Jenkins' Core Functionality: Extension Points and Plugin Development

> Learn how to modify Jenkins core functionality by developing plugins and leveraging extension points. Avoid editing source code directly for reliable customization.

- Repository: [Jenkins/jenkins](https://github.com/jenkinsci/jenkins)
- Tags: how-to-guide
- Published: 2026-08-01

---

**The most reliable way to modify Jenkins' core functionality is to implement an ExtensionPoint in a plugin rather than editing the core source code directly.**

Jenkins is architected around extensibility, allowing developers to modify behavior through the **ExtensionPoint** mechanism in the `jenkinsci/jenkins` repository. Instead of maintaining a fork of the core codebase, you can leverage the `@Extension` annotation and `ExtensionList` registry to inject custom logic that persists across upgrades.

## Understanding Jenkins' Extension Architecture

Jenkins core uses a sophisticated extension system defined in [`core/src/main/java/hudson/ExtensionPoint.java`](https://github.com/jenkinsci/jenkins/blob/main/core/src/main/java/hudson/ExtensionPoint.java). This marker interface identifies specific integration points where plugins can inject custom behavior.

The runtime discovery works through three core components:

- **@Extension** – Annotates concrete implementations so the `ExtensionFinder` can discover them at startup
- **ExtensionList** – The runtime registry holding all implementations of a given ExtensionPoint, accessible via `Jenkins.get().getExtensionList(...)` in [`core/src/main/java/jenkins/model/Jenkins.java`](https://github.com/jenkinsci/jenkins/blob/main/core/src/main/java/jenkins/model/Jenkins.java)
- **PluginManager** – Loads plugins and builds the classloader hierarchy in [`core/src/main/java/hudson/PluginManager.java`](https://github.com/jenkinsci/jenkins/blob/main/core/src/main/java/hudson/PluginManager.java), orchestrating the extension discovery process

When Jenkins starts, the `ExtensionFinder` mechanism scans the plugin classpath and populates `ExtensionList` objects for each ExtensionPoint type found.

## Extension vs. Patching: Two Approaches

You have two distinct strategies to modify Jenkins behavior:

**Extension via plugins (recommended)** – Create a new implementation of an existing ExtensionPoint, compile it as an `.hpi`/`.jpi` file, and install it through the Plugin Manager. This keeps the core untouched and survives upgrades.

**Patching the core (advanced)** – Clone the repository, modify Java source files directly (such as adding methods to [`Jenkins.java`](https://github.com/jenkinsci/jenkins/blob/main/Jenkins.java)), rebuild with Maven (`mvn clean install -DskipTests`), and replace the WAR file. This requires rebuilding and re-testing after every Jenkins release.

## How to Extend Jenkins Core via Plugins

Follow this workflow to safely modify core functionality:

1. **Identify the target ExtensionPoint** – Browse core classes like `jenkins.model.Queue`, `jenkins.security.SecurityListener`, or `hudson.tasks.Builder` to find the appropriate interface.

2. **Create a new implementation** – Implement the interface or extend the abstract class and annotate the class with `@Extension`.

3. **Inject dependencies** – Use Guice (`@Inject`) or standard Jenkins APIs (`Jenkins.get()`) to obtain services from the core.

4. **Control registration order** – If multiple implementations exist, use `@Extension(ordinal = N)` where higher ordinal values run later in the chain.

5. **Consume the extension** – Other components retrieve your implementation via `ExtensionList` or `ExtensionFinder` automatically.

## Code Examples

### Creating a Custom Build Step

The following example implements `hudson.tasks.Builder`, which extends ExtensionPoint, to create a custom build step:

```java
package org.example.jenkins;

import hudson.Extension;
import hudson.Launcher;
import hudson.model.AbstractBuild;
import hudson.model.BuildListener;
import hudson.model.AbstractProject;
import hudson.tasks.Builder;
import hudson.tasks.BuildStepDescriptor;
import org.kohsuke.stapler.DataBoundConstructor;

/** Simple builder that prints a message to the console. */
public class HelloWorldBuilder extends Builder {

    private final String message;

    @DataBoundConstructor
    public HelloWorldBuilder(String message) {
        this.message = message;
    }

    @Override
    public boolean perform(AbstractBuild<?, ?> build, Launcher launcher,
                           BuildListener listener) {
        listener.getLogger().println("Hello from custom builder: " + message);
        return true;
    }

    @Extension
    public static class DescriptorImpl extends BuildStepDescriptor<Builder> {
        public boolean isApplicable(Class<? extends AbstractProject> aClass) {
            return true; // works with any project type
        }

        public String getDisplayName() {
            return "Print Hello‑World Message";
        }
    }
}

```

The `@Extension` annotation registers this class with the `ExtensionFinder`, making it available in the Jenkins UI without modifying [`core/src/main/java/hudson/tasks/Builder.java`](https://github.com/jenkinsci/jenkins/blob/main/core/src/main/java/hudson/tasks/Builder.java).

### Overriding Security Listeners

To replace or augment security functionality, extend `SecurityListener` (defined in [`core/src/main/java/jenkins/security/SecurityListener.java`](https://github.com/jenkinsci/jenkins/blob/main/core/src/main/java/jenkins/security/SecurityListener.java)):

```java
package org.example.jenkins;

import hudson.Extension;
import jenkins.security.SecurityListener;
import hudson.security.ACL;
import hudson.security.ACLContext;

/** Override the default security checks. */
@Extension(ordinal = 100) // higher ordinal ensures this runs after the default listener
public class MySecurityListener extends SecurityListener {
    @Override
    public void authenticated2(ACL acl) {
        try (ACLContext ctx = ACL.as2(acl)) {
            // custom logic here
            // e.g., log every authentication event
            System.out.println("User authenticated: " + acl.getName());
        }
    }
}

```

Because `SecurityListener` is an ExtensionPoint, this implementation automatically joins the `ExtensionList<SecurityListener>` managed by the core.

### Accessing Extensions Programmatically

To retrieve all implementations of a specific ExtensionPoint from within your plugin or core code:

```java
import jenkins.model.Jenkins;
import hudson.ExtensionList;
import hudson.security.SecurityListener;

public class Demo {
    public static void listSecurityListeners() {
        ExtensionList<SecurityListener> list =
                Jenkins.get().getExtensionList(SecurityListener.class);
        for (SecurityListener sl : list) {
            System.out.println("Found SecurityListener: " + sl.getClass().getName());
        }
    }
}

```

The `Jenkins.get().getExtensionList(...)` method pulls from the unified registry that combines core and all installed plugin extensions.

## Key Source Files in the Jenkins Core

Understanding these files helps you navigate the extension system:

| File | Role |
|------|------|
| [`core/src/main/java/jenkins/model/Jenkins.java`](https://github.com/jenkinsci/jenkins/blob/main/core/src/main/java/jenkins/model/Jenkins.java) | Central singleton holding the ExtensionList registry and plugin manager |
| [`core/src/main/java/hudson/ExtensionPoint.java`](https://github.com/jenkinsci/jenkins/blob/main/core/src/main/java/hudson/ExtensionPoint.java) | Marker interface for every pluggable component |
| [`core/src/main/java/hudson/Extension.java`](https://github.com/jenkinsci/jenkins/blob/main/core/src/main/java/hudson/Extension.java) | Annotation that registers implementations with the extension system |
| [`core/src/main/java/hudson/ExtensionFinder.java`](https://github.com/jenkinsci/jenkins/blob/main/core/src/main/java/hudson/ExtensionFinder.java) | Scans plugin classloaders and builds ExtensionList objects at startup |
| [`core/src/main/java/hudson/ExtensionList.java`](https://github.com/jenkinsci/jenkins/blob/main/core/src/main/java/hudson/ExtensionList.java) | Runtime view of all implementations for a given ExtensionPoint |
| [`core/src/main/java/hudson/PluginManager.java`](https://github.com/jenkinsci/jenkins/blob/main/core/src/main/java/hudson/PluginManager.java) | Loads plugins, creates unified classloader, orchestrates extension discovery |
| [`core/src/main/java/hudson/model/Descriptor.java`](https://github.com/jenkinsci/jenkins/blob/main/core/src/main/java/hudson/model/Descriptor.java) | Base class for metadata and configuration UI of ExtensionPoint implementations |

## Summary

- **ExtensionPoint** provides the architecture for modifying Jenkins without touching core source code
- Use the **@Extension** annotation to register your implementations automatically
- Access extensions at runtime through **ExtensionList** retrieved via `Jenkins.get().getExtensionList()`
- The **PluginManager** and **ExtensionFinder** handle classloading and discovery during startup
- Prefer plugin-based extension over core patching to ensure upgrade compatibility
- Control extension ordering using the **ordinal** parameter in the @Extension annotation

## Frequently Asked Questions

### Can I modify Jenkins core functionality without rebuilding the WAR file?

Yes. Create a plugin that implements the appropriate ExtensionPoint interface from the core. Package it as an `.hpi` file and install it through the Jenkins Plugin Manager. The `ExtensionFinder` will discover your `@Extension`-annotated classes at runtime and integrate them into the core behavior without requiring a WAR rebuild.

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

**ExtensionPoint** is a marker interface that defines *what* can be extended in the core, such as `Builder` or `SecurityListener`. **Descriptor** is a companion class that provides metadata *about* an ExtensionPoint implementation, including the display name and configuration UI. Every ExtensionPoint implementation typically has a corresponding Descriptor inner class annotated with `@Extension`.

### How do I ensure my extension loads before or after another implementation?

Use the **ordinal** parameter in the `@Extension` annotation. Higher ordinal values cause the extension to be ordered later in the ExtensionList. For example, `@Extension(ordinal = 100)` loads after `@Extension(ordinal = 1)`. This is useful when you need to override or wrap behavior provided by other plugins or core implementations.

### Is it safe to directly patch Jenkins core source code?

Direct patching is only recommended for unavoidable cases such as critical bug fixes that cannot wait for a release. You must clone the repository, modify files like [`core/src/main/java/jenkins/model/Jenkins.java`](https://github.com/jenkinsci/jenkins/blob/main/core/src/main/java/jenkins/model/Jenkins.java), rebuild with Maven (`mvn clean install -DskipTests`), and replace the WAR. This approach requires rebuilding and extensive re-testing after every Jenkins upgrade, making it significantly harder to maintain than the plugin-based approach.