This file serves as a tutorial on how to get started with writing a Java plugin.
If you want to know more about plugins in general, please check out the How-To: Write a Plugin page. If you need a tutorial for using Key and KeySet in Java, please check out the How-To: Java kdb page.
Before we will take a look into how to write a plugin in Java, it is important to note, that there are two technologies needed:
process
plugin- JNA binding
The process
plugin is a special plugin, which allows using an external application as the implementation of a plugin.
To achieve this, the process
plugin spawns a child process for the external executable and then uses a simple protocol to relay any requested operations to this child process.
The details of how this protocol works are not important for writing a Java plugin.
All the details of the protocol are abstracted via the PluginProcess
class and the Plugin
interface.
If you do want to know the details of the process
protocol, take a look at the README of the process
plugin.
Java Native Access is Java technology (library), which like JNI allows a developer to run native code using only Java by providing access to native shared libraries. In order to use the JNA binding, it should be installed first. You can find more information on JNA’s GitHub page.
We provide a Java binding for Elektra's API via JNA. For more general information about the binding take a look at this document.
To write a plugin, you need to create an implementation of the org.libelektra.Plugin
interface.
This is very similar writing any other plugin, except that you use Java instead of C.
The standard API functions of the plugin API all have corresponding methods in the Plugin
interface:
int open(KeySet config, Key errorKey)
is the Java version ofelektraPluginOpen
int get(KeySet keySet, Key parentKey)
is the Java version ofelektraPluginGet
int set(KeySet keySet, Key parentKey)
is the Java version ofelektraPluginSet
int error(KeySet keySet, Key parentKey)
is the Java version ofelektraPluginError
int close(Key parentKey)
is the Java version ofelektraPluginClose
Additionally, there is a @Nonnull String getName()
method.
This method must return the unique name of the plugin.
In the C API this would be taken from the contract, but the process
protocol requires this separately, so we need a separate method for it.
Otherwise, there are a few differences between implementing a plugin in C and in Java:
- In C it is possible to implement only
elektraPluginGet
and leave the other functions unimplemented. Because of interface inheritance in Java, it is required to implement all 5 methodopen
,get
,set
,error
andclose
. Whether a method is actually supported, must be communicated via the contract. - In C the parent key of the contract depends on the plugins name.
For example, the contract for
dump
can be found undersystem:/elektra/modules/dump
and thedump
plugin returns it as such. However, in Java the parent key for the contract is alwayssystem:/elektra/modules/java
(you may use the constantPlugin.PROCESS_CONTRACT_ROOT
). The keys will be transformed via theprocess
protocol and plugin to match the normal expectations. - In C all functions a plugin exports (including
open
,get
,set
,error
,close
, but also additional ones) are registered in the contract undersystem:/elektra/modules/<plugin>/exports/<function>
with a function pointer key. Because we cannot provide a C function pointer to a Java function and becauseprocess
uses a child process for the Java code, we cannot export functions like that. This means a Java plugin cannot export additional functions. However, we must still define which functions are supported by the plugin. To this end, a Java plugin must setsystem:/elektra/modules/java/exports/has/<function> = 1
(where<function>
is one ofopen
,get
,set
,error
,close
) for all supported functions. - Methods that are not supported, should simply be implemented as
This is safe, because the method will not be called if the other steps are followed correctly. The exception here is the
throw new UnsupportedOperationException();
get
method. It must still be implemented and return the contract, when the parent is the same as or belowsystem:/elektra/modules/java
. Otherwise, it is safe to throwUnsupportedOperationException
.
Otherwise, the rules for return values and plugin behavior are the same as for a C plugin.
You can find a few examples for Java plugins in the folder src/bindings/jna/plugins/
.
We use separate Gradle projects for every plugin, but that is not a requirement.
But creating separate JAR files for each plugin, keeps down the size of the binaries that need to be loaded to mount a plugin.