Non-Visual Plugins (Java)
This article describes how to create a new non-visual Java plugin, what features it provides and how they can be used.
Getting Started
A Java-based Hydra plugin project consists of a three parts:
- Plugin Descriptor - a special class that provides plugin information like its name, version and, the most important part, reference to the class that actually implements the plugin itself.
- Plugin Interface(s) - one or more interfaces that are implemented by the plugin. Only methods defined in these interfaces can be called by the plugin host application.
- Plugin Implementation - a class that implements the plugin interface(s).
Hydra for Java plugins can be created in any language that supports JVM as a target platform (Java, Kotlin etc). The only requirement is that this language should be able to produce .jar
files.
Any .jar
file providing the 3 parts mentioned above can be loaded by Hydra host application.
Hydra for Java is shipped with plugin project templates for all 4 Elements family languages. The next section will describe a newly created NonVisual Plugin project based on this template.
NonVisual Plugin project review
This section contains a detailed review of Hydra for Java plugin project elements.
Plugin Descriptor
The purpose of this class is to provide the information required by a Hydra host application to properly load any use the plugin.
This class should implement the com.remobjects.hydra.PluginDescriptor
interface contained in the com.remobjects.hydra.jar
file shipped with Hydra for Java.
public class PluginDescriptor implements com.remobjects.hydra.PluginDescriptor {
@Override
public String getName() {
return "NonVisual Plugin";
}
...
@SuppressWarnings("rawtypes")
@Override
public Class getPluginClass() {
return PluginImplementation.class;
}
}
type
PluginDescriptor = public class(com.remobjects.hydra.PluginDescriptor)
public
property Name: String read 'NonVisual Plugin';
property Description: String read 'Hydra for Java non-visual plugin';
property UserData: String read '';
property MajorVersion: Int32 read 1;
property MinorVersion: Int32 read 0;
property PluginClass: &Class read typeOf(PluginImplementation);
end;
public class PluginDescriptor : com.remobjects.hydra.PluginDescriptor
{
public string Name
{
get
{
return "NonVisual Plugin";
}
}
...
public Class PluginClass
{
get
{
return typeof(PluginImplementation);
}
}
}
open class PluginDescriptor : com.remobjects.hydra.PluginDescriptor {
public var Name: String! {
get {
return "NonVisual Plugin"
}
}
...
public var PluginClass: Class! {
get {
return dynamicType(PluginImplementation)
}
}
}
As displayed above this class provides plugin metainformation like its name, description, version, as well as a reference to the class containing actual plugin implementation.
Plugin Interface
By its definition a plugin has to expose some implementation to its host application. This part of the plugin project defines methods that can be accessed by its host.
These class should directly or indirectly extend the com.remobjects.hydra.HYCrossPlatformNonVisualPlugin
interface contained in the com.remobjects.hydra.jar
file shipped with Hydra for Java.
public interface PluginInterface extends com.remobjects.hydra.HYCrossPlatformNonVisualPlugin {
// Define plugin interface methods here
}
type
PluginInterface = public interface(com.remobjects.hydra.HYCrossPlatformNonVisualPlugin)
// Define plugin interface methods here
end;
public interface PluginInterface : com.remobjects.hydra.HYCrossPlatformNonVisualPlugin
{
// Define plugin interface methods here
}
public protocol PluginInterface : com.remobjects.hydra.HYCrossPlatformNonVisualPlugin {
// Define plugin interface methods here
}
Methods defined in the plugin interface must follow the limitations described in the corresponding section.
Plugin Implementation
This is a class that implements the plugin interface(s). This class will be instantiated when the plugin will be requested by a host application.
public class PluginImplementation implements PluginInterface {
public void start() {
}
public void stop() {
}
public void pause() {
}
public void resume() {
}
}
type
PluginImplementation = public class(PluginInterface)
public
method start(); empty;
method stop(); empty;
method pause(); empty;
method resume(); empty;
end;
public class PluginImplementation : PluginInterface
{
public void start()
{
}
public void stop()
{
}
public void pause()
{
}
public void resume()
{
}
}
open class PluginImplementation : PluginInterface {
public func start() {
}
public func stop() {
}
public func pause() {
}
public func resume() {
}
}
Note a set of 4 methods (start
, stop
, pause
, resume
) that are defined in the PluginImplementation
class. These methods are defined in the com.remobjects.hydra.HYCrossPlatformNonVisualPlugin
interface
and are a set of methods implemented by any non-visual plugin regardless of its platform (.NET, Delphi or Java).
These methods can be called on a plugin even if its interface has not been imported into the host application project (see below).
Plugin method limitations
The features set supported by the NonVisual Java plugins is more limited that one supported by Delphi or .NET plugins.
Only a following Java types can be used as plugin method parameter or result types:
Boolean
,Boolean[]
Char
,Char[]
String
,String[]
Byte
,Byte[]
Short
,Short[]
Integer
,Integer[]
Long
,Long[]
Float
,Float[]
Double
,Double[]
This limitation means that complex types have to be serialized or decomposed to be passed to or from plugin. Also there is no support of any form of callbacks or plugin-raised events.
Using the Plugin
Loading Plugin
The following prerequisites have to be met to allow a host application to load Java plugins:
- JVM installed. Its bitness should match the one of the host application. This means that if the host application is built as a x86 app then to load a Java plugin it will require a x86 version of JVM.
com.remobjects.hydra.jar
file should be placed next to the host application assembly. This.jar
file contains a Java code required to load and manage Hydra for Java plugins.
Note: The com.remobjects.hydra.jar
file used to load Hydra for Java plugins should match the com.remobjects.hydra.jar
file used to build the plugins.
Once prerequisites are met all what is required to load a Java plugin is to call the LoadModule
method of the host's ModuleManager
instance:
moduleManager.LoadModule("com.hydra.sample.nonvisual.plugin.jar");
moduleManager.LoadModule('com.hydra.sample.nonvisual.plugin.jar');
moduleManager.LoadModule("com.hydra.sample.nonvisual.plugin.jar");
moduleManager.LoadModule('com.hydra.sample.nonvisual.plugin.jar')
Note: Path to the .jar file should contain only alphanumeric symbols. In case it would contain chars like #
plugin loading will fail with obscure Class not found
message.
Default Interface
Once the plugin is loaded it can be instantiated and used via the usual Hydra API. Note that NonVisual plugin by default supports methods Start
, Stop
, Pause
, Resume
.
var pluginInstance = ((INonVisualPlugin)(module.CreateInstance(plugin)));
pluginInstance.Start();
var pluginInstance := INonVisualPlugin(&module.CreateInstance(plugin));
pluginInstance.Start();
var pluginInstance = module.CreateInstance(plugin) as INonVisualPlugin;
pluginInstance.Start();
var pluginInstance = (module.CreateInstance(plugin) as? INonVisualPlugin)
pluginInstance.Start()
Custom Interface
This section shows how to access methods defined in the plugin's custom interface.
To access plugin's custom interface methods this plugin should be first imported into the host application. This can be done via a button in the solution explorer's header. Press it and select the plugin .jar in the dialog opened.
Plugin import procedure will create a code file containing interface definitions(s) and a wrapper class that will be used by Hydra to perform JNI calls.
public interface ICalculatorPlugin : RemObjects.Hydra.CrossPlatform.IHYCrossPlatformNonVisualPlugin
{
double sumValues(double[] arg0);
}
[RemObjects.Hydra.CrossPlatform.JavaClass("com/hydra/sample/nonvisual/plugin/NonVisualPlugin")]
public partial class NonVisualPlugin : RemObjects.Hydra.Host.Java.JavaNonVisualPluginWrapper, ICalculatorPlugin
{
public NonVisualPlugin(RemObjects.Hydra.Host.Java.IJavaReflection jvm, System.IntPtr classId) : base(jvm, classId)
{
// base(jvm, classId)
}
public double sumValues(double[] arg0)
{
System.IntPtr methodId = this.fJVM.GetMethodId(this.fClassId, "sumValues", "([D)D");
return this.fJVM.CallMethod<double>(this.fInstanceId, methodId, (arg0 as object));
}
}
It is not recommended to manually edit this file.
Note: After import the plugin .jar file will be cached by IDE integration code, possibly locking the .jar file and preventing importer code from accessing updated .jar file. Visual Studio restart is required to clear that cache.
Once the plugin is imported its methods can be accessed by casting the created plugin instance to the imported interface:
ICalculatorPlugin calculator = ((ICalculatorPlugin)(module.CreateInstance(plugin)));
calculator.sumValues(((double[])({1, 2, 3, 4})));
var calculator := ICalculatorPlugin(&module.CreateInstance(plugin));
calculator.sumValues([ 1.0, 2.0, 3.0, 4.0 ]);
var calculator = module.CreateInstance(plugin) as ICalculatorPlugin;
calculator.sumValues(new double[] { 1.0, 2.0, 3.0, 4.0 });
var calculator: ICalculatorPlugin! = (module.CreateInstance(plugin) as? ICalculatorPlugin)
calculator.sumValues(([1, 2, 3, 4] as? Double[]))