Skip to main content

Overview

Services provide a powerful mechanism for plugins to expose functionality to other plugins. The service architecture enables loose coupling between plugins while maintaining strong contracts through Java interfaces.

Service Architecture

The service system is managed by the ServiceManager class, which maintains a registry of service interfaces and their implementations. Reference: ~/workspace/source/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/mgr/ServiceManager.java:35

Key Components

  • Service Interface: A Java interface defining the contract
  • Service Provider: A plugin implementing the service interface
  • Service Consumer: A plugin using the service
  • ServiceManager: Manages registration and lookup

Defining Service Interfaces

Service interfaces are standard Java interfaces with no special requirements:
package ghidra.app.services;

public interface ClipboardService {
    void registerClipboardContentProvider(ClipboardContentProviderService service);
    void deRegisterClipboardContentProvider(ClipboardContentProviderService service);
}
Reference: ~/workspace/source/Ghidra/Features/Base/src/main/java/ghidra/app/services/ClipboardService.java:18

Service Interface Guidelines

Keep It Focused

Service interfaces should have a single, well-defined responsibility

Document Contracts

Clearly document method behavior, parameters, and return values

Version Carefully

Consider backward compatibility when modifying existing services

Use Standard Types

Prefer standard types over plugin-specific classes in signatures

Service Annotations

@ServiceInfo

Optionally annotate service interfaces with @ServiceInfo to specify metadata:
@ServiceInfo(
    defaultProvider = MyServicePlugin.class,
    description = "Provides clipboard functionality"
)
public interface ClipboardService {
    // Service methods
}
The defaultProvider property specifies which plugin should be auto-loaded when this service is required but no provider is currently loaded.

Providing Services

Plugins can provide services in two ways:

Method 1: Direct Implementation

Implement the service interface directly in your plugin class:
@PluginInfo(
    category = "Clipboard",
    servicesProvided = { ClipboardService.class }
)
public class ClipboardPlugin extends Plugin implements ClipboardService {
    
    public ClipboardPlugin(PluginTool tool) {
        super(tool);
        // Service is automatically registered
    }
    
    @Override
    public void registerClipboardContentProvider(ClipboardContentProviderService service) {
        // Implementation
    }
    
    @Override
    public void deRegisterClipboardContentProvider(ClipboardContentProviderService service) {
        // Implementation
    }
}
Reference: ~/workspace/source/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/Plugin.java:249
When your plugin directly implements a service interface, do NOT call registerServiceProvided() for that service. The framework automatically registers it.

Method 2: Delegated Implementation

Delegate service implementation to a separate object:
@PluginInfo(
    category = "Analysis",
    servicesProvided = { AnalysisService.class }
)
public class AnalysisPlugin extends Plugin {
    
    public AnalysisPlugin(PluginTool tool) {
        super(tool);
        
        // Create service implementation object
        AnalysisService serviceImpl = new AnalysisServiceImpl(this);
        
        // Register the service
        registerServiceProvided(AnalysisService.class, serviceImpl);
    }
}

class AnalysisServiceImpl implements AnalysisService {
    private final AnalysisPlugin plugin;
    
    AnalysisServiceImpl(AnalysisPlugin plugin) {
        this.plugin = plugin;
    }
    
    // Implement service methods
}
Reference: ~/workspace/source/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/Plugin.java:637

Dynamic Service Registration

Services can be registered after plugin construction:
public class MyPlugin extends Plugin {
    
    private void enableFeature() {
        MyService service = new MyServiceImpl();
        registerDynamicServiceProvided(MyService.class, service);
    }
    
    private void disableFeature() {
        deregisterService(MyService.class, serviceInstance);
    }
}
Reference: ~/workspace/source/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/Plugin.java:648

Consuming Services

Declaring Dependencies

Declare required services in the @PluginInfo annotation:
@PluginInfo(
    category = "CodeBrowser",
    servicesRequired = { 
        ProgramManager.class,
        ClipboardService.class,
        NavigationHistoryService.class 
    }
)
public class CodeBrowserPlugin extends Plugin {
    private ProgramManager programManager;
    private ClipboardService clipboardService;
    
    @Override
    protected void init() {
        // Acquire services after dependencies are met
        programManager = tool.getService(ProgramManager.class);
        clipboardService = tool.getService(ClipboardService.class);
    }
}
Reference: ~/workspace/source/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/Plugin.java:518

Retrieving Services

Single Implementation

Get the first available implementation:
@Override
protected void init() {
    MyService service = tool.getService(MyService.class);
    if (service != null) {
        service.doSomething();
    }
}
Reference: ~/workspace/source/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/mgr/ServiceManager.java:147

Multiple Implementations

Get all available implementations:
@Override
protected void init() {
    MyService[] services = tool.getServices(MyService.class);
    for (MyService service : services) {
        service.doSomething();
    }
}
Reference: ~/workspace/source/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/mgr/ServiceManager.java:163

Service Lifecycle

Service Added Notifications

Plugins can respond when services become available:
public class MyPlugin extends Plugin {
    
    @Override
    public void serviceAdded(Class<?> interfaceClass, Object service) {
        if (interfaceClass == ProgramManager.class) {
            // ProgramManager service is now available
            setupProgramManager((ProgramManager) service);
        }
    }
}
Reference: ~/workspace/source/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/Plugin.java:493

Service Removed Notifications

Handle service removal gracefully:
public class MyPlugin extends Plugin {
    private ProgramManager programManager;
    
    @Override
    public void serviceRemoved(Class<?> interfaceClass, Object service) {
        if (interfaceClass == ProgramManager.class) {
            // ProgramManager service is being removed
            cleanupProgramManager();
            programManager = null;
        }
    }
}
Reference: ~/workspace/source/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/Plugin.java:507

Dependency Management

Automatic Dependency Resolution

Ghidra automatically resolves service dependencies:
  1. Plugins are constructed in dependency order
  2. Plugins wait for required services to become available
  3. init() is called only when all dependencies are met
  4. Plugins fail to load if dependencies cannot be satisfied

Checking Service Availability

if (tool.isService(ProgramManager.class)) {
    ProgramManager pm = tool.getService(ProgramManager.class);
    // Use the service
}

Optional Dependencies

For optional services, don’t declare them in servicesRequired:
@Override
protected void init() {
    // Optional service - may be null
    OptionalService service = tool.getService(OptionalService.class);
    if (service != null) {
        enableOptionalFeature(service);
    }
}

Common Service Patterns

Singleton Services

Most services have a single provider:
@PluginInfo(
    servicesProvided = { ProgramManager.class }
)
public class ProgramManagerPlugin extends Plugin implements ProgramManager {
    // Single instance provides the service
}

Multiple Provider Services

Some services allow multiple providers:
// Multiple plugins can provide ContentTypeService
@PluginInfo(
    servicesProvided = { ContentTypeService.class }
)
public class PdfTypePlugin extends Plugin implements ContentTypeService {
    // Provides PDF content type support
}

@PluginInfo(
    servicesProvided = { ContentTypeService.class }
)
public class ImageTypePlugin extends Plugin implements ContentTypeService {
    // Provides image content type support
}
Consumers can get all providers:
ContentTypeService[] providers = tool.getServices(ContentTypeService.class);
for (ContentTypeService provider : providers) {
    if (provider.canHandle(file)) {
        provider.process(file);
    }
}

Layered Services

Services can depend on other services:
@PluginInfo(
    servicesRequired = { DataTypeManagerService.class },
    servicesProvided = { DataTypeAnalysisService.class }
)
public class DataTypeAnalysisPlugin extends Plugin 
        implements DataTypeAnalysisService {
    
    private DataTypeManagerService dtmService;
    
    @Override
    protected void init() {
        dtmService = tool.getService(DataTypeManagerService.class);
    }
    
    @Override
    public void analyzeDataTypes(Program program) {
        // Use dtmService to perform analysis
    }
}

Service Best Practices

Keep service interfaces focused. If a service has multiple responsibilities, consider splitting it into multiple services.
// Good - focused interfaces
interface SearchService { /* search methods */ }
interface IndexService { /* indexing methods */ }

// Avoid - mixed responsibilities
interface SearchAndIndexService { /* both */ }
Always check for null when retrieving services that aren’t required dependencies.
OptionalService service = tool.getService(OptionalService.class);
if (service != null) {
    service.doWork();
}
Service methods may be called from multiple threads. Design services to be thread-safe or document threading requirements.
public class ThreadSafeService implements MyService {
    private final Object lock = new Object();
    
    @Override
    public void doWork() {
        synchronized (lock) {
            // Thread-safe implementation
        }
    }
}
Document and handle errors appropriately in service methods.
/**
 * Processes the given data.
 * @param data the data to process
 * @throws IllegalArgumentException if data is invalid
 * @throws IOException if processing fails
 */
void processData(Data data) throws IOException;

ServiceManager API

The ServiceManager class provides the core service registry functionality:
public class ServiceManager {
    // Add a service to the registry
    public <T> void addService(Class<? extends T> interfaceClass, T service);
    
    // Remove a service from the registry
    public void removeService(Class<?> interfaceClass, Object service);
    
    // Get first service implementation
    public <T> T getService(Class<T> interfaceClass);
    
    // Get all service implementations
    public <T> T[] getServices(Class<T> interfaceClass);
    
    // Check if service exists
    public boolean isService(Class<?> serviceInterface);
    
    // Add/remove service listeners
    public void addServiceListener(ServiceListener listener);
    public void removeServiceListener(ServiceListener listener);
}

Common Services in Ghidra

Here are some frequently used services:
ServiceDescription
ProgramManagerManages open programs and program state
ClipboardServiceProvides clipboard operations
NavigationHistoryServiceManages navigation history
GoToServiceHandles navigation to addresses
CodeViewerServiceProvides code listing views
DataTypeManagerServiceManages data types
BookmarkServiceManages program bookmarks
ConsoleServiceProvides console output

Troubleshooting

Plugin Won’t Load

If your plugin fails to load due to missing services:
  1. Check the servicesRequired in @PluginInfo
  2. Verify the required service plugins are installed
  3. Check for circular dependencies
  4. Review the application log for specific errors

Service Returns Null

// Check if service is required but missing
MyService service = tool.getService(MyService.class);
if (service == null) {
    Msg.warn(this, "MyService is not available");
    return;
}

Multiple Service Providers

When multiple plugins provide the same service, getService() returns an arbitrary one. Use getServices() to get all providers.

Build docs developers (and LLMs) love