$darkmode
Elektra 0.11.0
|
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
pluginThe 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 of elektraPluginOpen
int get(KeySet keySet, Key parentKey)
is the Java version of elektraPluginGet
int set(KeySet keySet, Key parentKey)
is the Java version of elektraPluginSet
int error(KeySet keySet, Key parentKey)
is the Java version of elektraPluginError
int close(Key parentKey)
is the Java version of elektraPluginClose
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:
elektraPluginGet
and leave the other functions unimplemented. Because of interface inheritance in Java, it is required to implement all 5 method open
, get
, set
, error
and close
. Whether a method is actually supported, must be communicated via the contract.dump
can be found under system:/elektra/modules/dump
and the dump
plugin returns it as such. However, in Java the parent key for the contract is always system:/elektra/modules/java
(you may use the constant Plugin.PROCESS_CONTRACT_ROOT
). The keys will be transformed via the process
protocol and plugin to match the normal expectations.open
, get
, set
, error
, close
, but also additional ones) are registered in the contract under system:/elektra/modules/<plugin>/exports/<function>
with a function pointer key. Because we cannot provide a C function pointer to a Java function and because process
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 set system:/elektra/modules/java/exports/has/<function> = 1
(where <function>
is one of open
, get
, set
, error
, close
) for all supported functions.get
method. It must still be implemented and return the contract, when the parent is the same as or below system:/elektra/modules/java
. Otherwise, it is safe to throw UnsupportedOperationException
.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.