-
Notifications
You must be signed in to change notification settings - Fork 0
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.
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.
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>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
Implement this interface to add a new catalog type to the Payloadbuilder engine. A catalog provides:
- A
Catalogimplementation defining available tables and their schemas - An operator that scans or filters data from the source
- Optional configuration component for the Options dialog
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.
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.
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.
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).
Implement this interface to add a new section to the Options dialog. The component is shown alongside built-in configuration sections.
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.
Implement this interface to expose your plugin's functionality as tools accessible through the Model Context Protocol server built into Queryeer.
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
}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.
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
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));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());