Skip to content

Plugin System

Marcus Henriksson edited this page Apr 2, 2026 · 1 revision

Plugin System

Queryeer is built around a plugin architecture. Almost every major feature — query engines, output types, export formats, AI providers — is implemented as a plugin. This page explains how the system works and how to write your own plugin.


How Plugins Are Discovered

On startup, Queryeer scans the plugins/ folder using ClassGraph. Any JAR in that folder is loaded into an isolated classloader and scanned for classes that implement the Queryeer extension interfaces. Matching classes are instantiated automatically — no registration file or configuration is needed.

Dependencies that should be shared between multiple plugins (for example, a JDBC driver used by two different engine plugins) should be placed in shared/ instead of bundled inside each plugin JAR.


Extension Points

All extension interfaces live in the queryeer-api module, which is published to Maven Central. Add it as a provided dependency in your plugin's build.

<dependency>
    <groupId>com.queryeer</groupId>
    <artifactId>queryeer-api</artifactId>
    <version><!-- latest version --></version>
    <scope>provided</scope>
</dependency>

IQueryEngine — Add a Query Engine

Implement this interface to add a new data source type. The interface covers:

  • execute(query, outputWriter) — Run a query and write rows to the output
  • cancel() — Stop an in-progress query
  • getMetadata() — Return catalog metadata for code completion
  • getConfigComponent() — Return a Swing component shown in the Options dialog
  • getName() — Display name shown in the engine selector

ICatalogExtensionFactory — Add a Payloadbuilder Catalog

Implement this interface to add a new catalog type to the Payloadbuilder engine. A catalog provides:

  • A Catalog implementation defining available tables and their schemas
  • An operator that scans or filters data from the source
  • Optional configuration component for the Options dialog

IOutputExtension — Add an Output Type

Implement this interface to add a new way to display query results. The output receives a stream of rows and renders them in a Swing component.

IOutputFormatExtension — Add an Export Format

Implement this interface to add a new file export format. The extension receives the result rows and writes them to a stream in the desired format.

IAIAssistantProvider — Add an AI Chat Provider

Implement this interface to integrate a new AI service into the AI chat panel. The provider receives the user message plus optional context (query text, query results) and returns the assistant's reply.

IAIContextItem — Add AI Context Items

Implement this interface to expose additional context that users can optionally include when sending a message to the AI assistant (e.g., schema information, recent errors, environment details).

IConfigurable — Add a Settings Section

Implement this interface to add a new section to the Options dialog. The component is shown alongside built-in configuration sections.

IExtensionAction — Add Toolbar or Menu Actions

Implement this interface to register custom actions that appear in the toolbar or context menus. Actions can be engine-specific (only shown when a particular engine is active) or global.

IMcpHandler — Expose Functionality via MCP

Implement this interface to expose your plugin's functionality as tools accessible through the Model Context Protocol server built into Queryeer.


Dependency Injection

Plugin classes are instantiated via constructor injection. Declare the services you need as constructor parameters and Queryeer's container will supply them.

Available injectable services:

Service Interface Description
Event bus IEventBus Publish and subscribe to application events
Configuration IConfig Read and write JSON-backed user configuration
Crypto ICryptoService Encrypt and decrypt sensitive values (Jasypt)
Action registry IActionRegistry Register global actions
Dialog factory IDialogFactory Create standard Queryeer dialogs
Icon factory IIconFactory Access to bundled Ikonli icons
Template service ITemplateService Freemarker template rendering
Payloadbuilder IPayloadbuilderService Execute Payloadbuilder queries programmatically
Expression evaluator IExpressionEvaluator Evaluate expressions in the Queryeer expression language
Graph visualization IGraphVisualizationService Programmatically build and display graphs
Query file provider IQueryFileProvider Access currently open query files and their state

Example constructor:

public class MyQueryEngine implements IQueryEngine {

    private final IConfig config;
    private final ICryptoService cryptoService;

    @Inject
    public MyQueryEngine(IConfig config, ICryptoService cryptoService) {
        this.config = config;
        this.cryptoService = cryptoService;
    }

    // ... interface implementation
}

Packaging a Plugin

The recommended way to package a plugin is with the Maven Assembly Plugin to produce a single JAR that includes all runtime dependencies (excluding queryeer-api, which is provided).

<plugin>
    <artifactId>maven-assembly-plugin</artifactId>
    <configuration>
        <descriptorRefs>
            <descriptorRef>jar-with-dependencies</descriptorRef>
        </descriptorRefs>
    </configuration>
    <executions>
        <execution>
            <phase>package</phase>
            <goals><goal>single</goal></goals>
        </execution>
    </executions>
</plugin>

Place the resulting -jar-with-dependencies.jar in the plugins/ folder and restart Queryeer.


Plugin Directory Layout

queryeer-<version>/
├── plugins/
│   ├── my-engine-plugin-1.0-jar-with-dependencies.jar
│   └── my-catalog-plugin-2.0-jar-with-dependencies.jar
└── shared/
    └── mssql-jdbc-12.x.jar   # shared JDBC driver, available to all plugins

Event Bus

The IEventBus service provides a lightweight pub/sub mechanism for decoupling components. Plugins can publish events and subscribe to events published by other plugins or by the Queryeer core.

// Subscribe
eventBus.subscribe(QueryExecutedEvent.class, event -> {
    System.out.println("Query finished: " + event.getRowCount() + " rows");
});

// Publish
eventBus.publish(new MyCustomEvent(data));

Configuration API

The IConfig service provides a simple typed API for persisting plugin configuration as JSON:

// Read
MySettings settings = config.get("my-plugin", MySettings.class, MySettings::new);

// Write
settings.setHost("localhost");
config.save("my-plugin", settings);

Passwords and other sensitive values should be encrypted before saving:

String encrypted = cryptoService.encrypt(plainPassword);
settings.setPassword(encrypted);

And decrypted when read:

String plain = cryptoService.decrypt(settings.getPassword());