Extension providers
The extensions mechanism lets one piece of code declare a pluggable contract and any other piece of code contribute a provider. It is the canonical way the platform itself surfaces menu entries, perspectives, and views, and it is open to user projects.
Two coexisting forms:
- Typed Java - an extension point is a plain Java interface; a contribution is a
@Componentbean that implements it. Consumers receive all contributions via collection injection (List<SampleExtensionPoint>) orExtensions.find(Class). Preferred for Java code. - String-keyed artefacts -
*.extensionpointdeclares a named point;*.extensionregisters a JS / TS module against the named point. Discovered withExtensions.getExtensions(String). Used by TypeScript-side extensions and by older code.
Typed Java extensions
Declare the contract
An extension point is just a plain Java interface - no annotation:
package demo.extension;
public interface SampleExtensionPoint {
String describe();
}Contribute an implementation
A contribution is a @Component bean that implements the interface - no extra annotation. The contribution's name is its @Component name:
package demo.extension;
import org.eclipse.dirigible.sdk.component.Component;
@Component("sample-contribution")
public class SampleContribution implements SampleExtensionPoint {
@Override
public String describe() {
return "Hello from SampleContribution!";
}
}Consumers receive instances typed as the interface - no reflection, no Map payloads.
Consume at runtime
Java
Collection injection (preferred). Because every contribution is a @Component, the Spring-style way to receive all implementations is to inject a List<SampleExtensionPoint> through the constructor of a @Controller or @Component. The container populates it with every bean assignable to the interface:
package demo.extension;
import java.util.List;
import org.eclipse.dirigible.sdk.http.Controller;
import org.eclipse.dirigible.sdk.http.Get;
@Controller
public class InjectingConsumer {
private final List<SampleExtensionPoint> contributions;
public InjectingConsumer(List<SampleExtensionPoint> contributions) {
this.contributions = contributions;
}
@Get("/injected-contributions")
public List<String> list() {
return contributions.stream()
.map(SampleExtensionPoint::describe)
.toList();
}
}Extensions.find(Class) (programmatic alternative). When you cannot inject - or want to look up providers at an arbitrary point - Extensions.find(Class) returns them directly:
package demo.extension;
import java.util.List;
import org.eclipse.dirigible.sdk.extensions.Extensions;
import org.eclipse.dirigible.sdk.http.Controller;
import org.eclipse.dirigible.sdk.http.Get;
@Controller
public class ExtensionConsumer {
@Get("/contributions")
public List<String> listContributions() throws Exception {
return Extensions.find(SampleExtensionPoint.class)
.stream()
.map(SampleExtensionPoint::describe)
.toList();
}
}Extensions.find also works across runtimes and remains available for back-compatibility, so prefer it when the consumer must see providers that injection cannot reach.
TypeScript / JavaScript
TypeScript-side extensions use the string-keyed artefacts described below; Extensions.getExtensions(String) returns the registry paths of the contributing modules:
import { Extensions } from "@aerokit/sdk/extensions";
const modules = Extensions.getExtensions("my-app-menu-items");
for (const m of modules) {
// m is the registry path of a contributing module; load and invoke as appropriate
}Sample project: dirigiblelabs/sample-java-extension-decorator - SampleExtensionPoint (plain interface) + SampleContribution (@Component("sample-contribution")), consumed both by InjectingConsumer (constructor List<SampleExtensionPoint> collection injection) and ExtensionConsumer (Extensions.find). SDK reference: /sdk/.
String-keyed artefacts
Declare a point
{
"name": "my-app-menu-items",
"description": "Items shown under the My App menu"
}Save as my-app-menu-items.extensionpoint.
Contribute a provider
{
"extensionPoint": "my-app-menu-items",
"module": "demo/extensions/customer-menu"
}Save as customer-menu.extension. Cross-link: /help/artefacts/extensibility/extension.
Consume at runtime
import { Extensions } from "@aerokit/sdk/extensions";
const modules = Extensions.getExtensions("my-app-menu-items");
for (const m of modules) {
// m is the registry path of a contributing module; load and invoke as appropriate
}When to use this
- IDE chrome - pluggable menu items, perspectives, views.
- Cross-project workflows - one project owns a flow, others contribute steps.
- Anywhere "find every provider of X" is the natural shape.
For typed wiring within a single project, prefer dependency injection instead.