Developing Plugins#
Getting Started#
Concourse plugins must be implemented as Java classes that extend
com.cinchapi.concourse.server.plugin.Plugin. To get started,
create a Java project that depends on the concourse-plugin-core
framework.
Create a build.gradle File#
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | |
Download Dependencies and Generate IDE Metadata#
1 | |
Implement the Plugin#
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | |
Any instance methods added to the plugin class are dynamically added to the Concourse API and can be invoked by clients.
Create the Plugin Package#
1 | |
Check the build/distributions directory for the generated .zip
plugin package.
Accessing Concourse within a Plugin#
Concourse plugins have privileged access to the database using a
special API. To interact with Concourse, use the runtime method
inside the plugin class.
1 2 3 4 5 6 7 8 9 | |
If you need to access Concourse from a class that does not extend
Plugin (e.g., a utility class), interact with the runtime by
calling PluginRuntime.getRuntime().
Plugin Storage#
Plugins can use persistent storage that survives installs,
upgrades, and uninstalls. Access storage via the storage() method:
1 2 3 4 5 6 7 | |
Plugin storage is environment-scoped, meaning each environment gets its own isolated storage directory. Storage is located at:
1 | |
The plugin_data_directory is configured in
concourse.yaml (default:
~/concourse/plugin-data).
Deprecated storage APIs#
Older plugins used the per-request storage methods on
PluginStateContainer:
| Deprecated method | Replacement |
|---|---|
data() |
storage() |
localStorage() |
storage() |
localStorage(String) |
storage() scoped to a sub-key |
memoryStorage() |
Use an in-memory map held on the plugin instance |
tempStorage() |
Create a Bucket directly with a temporary path |
These methods still work but may be removed in a future major
release. New plugins should use storage() exclusively and
build any transient or in-memory state on ordinary JVM objects
owned by the plugin instance.
Background Threads#
Plugins run in a separate JVM from the server, so they can safely perform long-running operations without affecting server performance.
Each plugin has access to the host Concourse instance for background processing.
Cross-Version Compatibility#
Plugins compiled against one version of Java can run on servers
using a different version. For example, a plugin compiled with
Java 21 can work with a server running Java 8 when using the
external bootstrapper (the default).
Custom Java Runtime#
You can specify a custom Java runtime for a plugin in its configuration:
1 2 | |
Or specify just the binary:
1 | |
Plugin Lifecycle#
- Install: The plugin
.zippackage is extracted to the plugins directory. - Activate: The server discovers plugin classes using the configured bootstrapper (external or internal).
- Launch: A separate JVM is started for the plugin with IPC channels.
- Run: The plugin processes requests from clients and can call back to the server.
- Stop: The plugin is gracefully shut down when the server stops.
If a plugin crashes, the server automatically attempts to restart it with exponential backoff (up to 5 attempts). Each retry waits longer than the previous one so that a misbehaving plugin does not thrash the system.
Permanent Failed State#
If a plugin exhausts its restart budget without a clean startup, it is moved into a permanent failed state. Subsequent invocations of its methods fail immediately with a descriptive error — the server does not continue trying to restart it, and does not hang waiting for a response. Recovering a permanently-failed plugin requires administrator intervention: fix the underlying cause and then reinstall or reactivate the plugin.
This behavior protects the server from runaway plugins while making the failure mode easy to diagnose — a permanent failure surfaces as a clear error instead of a silently-stuck invocation.
Plugin Invocation#
Clients invoke plugin methods through the standard API:
1 2 3 4 5 | |
Plugin methods have a default timeout of 5 minutes. If a method takes longer, the invocation fails with a timeout exception.