First of all, this is not a bad meaning towards the team of remobjects and responsible members of Hydra. I have written a few post the last days and weeks and always got a very fast and friendly response with upmost respect and being very helpful and reflective. But I´m running a bit out of time evaluating hydra for a upcoming project and either i discard the idea and start building a delphi rest-server soon or get hydra together with java working.
I want to introduce hydra in our company and no matter how much i evaluate and test the framework towards java it feels like i hit a wall. I know Hydra is very solid in .NET and in communication through delphi platforms but it still feels like java is either not used very much through consumer or I´m doing something fundamental wrong.
About my environment:
I have installed Windows 10 64-Bit latest Updates always included
I have installed Eclipse Version: 2019-09 R (4.13.0)
I have installed Embarcadero® Delphi 10.2 Version 25.0.29899.2631 Enterprise Edition
I have installed RemObjects Hydra - 6.2.101.1239 (beta) since there has been some fixes about interface generation in delphi. I also can try to install last official build to reproduce the issues.
As far it concerns Java i run AdoptOpenJDK 13 64 Bit Hotspot Edition in order to get Eclipse running while Java_Home points to JDK 1.8.0.241 32-Bit. This step were required that Hydra with Delphi can generate interfaces and Eclipse can be executed (they dropped 32-Bit support 2010). I generate JAR-Files via Eclipse using JDK 1.8.0.241 32-Bit as environment.
I followed your Guide https://docs.hydra4.com/Plugins/NonVisualPluginsJava/ and generated a simple plugin:
package xxx.hydra.plugins;
public interface PluginInterface extends com.remobjects.hydra.HYCrossPlatformNonVisualPlugin
{
public boolean GetBoolean();
public String GetString();
public byte GetByte();
public int GetInt();
public long GetLong();
public float GetFloat();
public double GetDouble();
public short GetShort();
public char GetChar();// TODO : check if there would be possible to pass a Subobject as interface if its based on an Interface?
// public ISubObjectInterface GetInterface();
}
i implemented a Plugin implementation as followed:
package xxx.hydra.plugins;
public class PluginImplementation implements PluginInterface
{@Override
public void pause() {
}@Override
public void resume() {}
@Override
public void start() {}
@Override
public void stop() {}
@Override
public boolean GetBoolean() {
return true;
}@Override
public String GetString() {
return “Hello World”;
}@Override
public byte GetByte() {
return 0x8;
}@Override
public int GetInt() {
return 27;
}@Override
public long GetLong() {
return 250;
}@Override
public float GetFloat() {
return 29;
}@Override
public double GetDouble() {
return 15.9;
}@Override
public short GetShort() {
return 2;
}@Override
public char GetChar() {
return ‘X’;
}
}The values are just for testing.
I wrote a simple plugin descriptor as followed :
package xxx.hydra.plugins;
public class PluginDescriptor implements com.remobjects.hydra.PluginDescriptor {
public String getName() { return "NonVisualPlugin"; } @SuppressWarnings("rawtypes") public Class getPluginClass() { return PluginImplementation.class; }
public String getDescription() {
// TODO Auto-generated method stub
return null;
}public int getMajorVersion() {
// TODO Auto-generated method stub
return 1;
}public int getMinorVersion() {
// TODO Auto-generated method stub
return 2;
}public String getUserData() {
// TODO Auto-generated method stub
return “Test Data”;
}
}
added com.remobjects.hydra as external jar to project and export the file as “xxx.hydra.plugins.jar”.
I added com.remobjects.hydra.jar into same directory as application and plugin is stored.
In Delphi, if i have only one Method (either GetString or GetNumber) as interface it seems to work to generate an interface:
unit xxx_hydra_plugins_Import;
interface
uses
uHYCrossPlatformInterfaces,
uHYJavaTypes,
uHYJavaNonVisualPluginWrapper;type
{ Forward declarations }
IPluginInterface = interface;
TPluginImplementationWrapper = class;IPluginInterface = interface(IHYCrossPlatformNonVisualPlugin)
[‘{F8563254-6D04-4699-A843-CD0B71F503B3}’]
function GetNumber: JInt;
end;TPluginImplementationWrapper = class(THYJavaNonVisualPluginWrapper, IPluginInterface)
public
function GetNumber: JInt;
end;implementation
uses
uHYCrossPlatformModuleController;function TPluginImplementationWrapper.GetNumber: JInt;
var
lmethodId: JMethodID;
largs: JValueDynArray;
begin
lmethodId := Self.JVM.Reflection.GetMethodId(Self.ClassID, ‘GetNumber’, ‘()I’);
System.SetLength(largs, 0);
result := Self.JVM.Reflection.CallIntMethod(Self.InstanceID, lmethodId, largs);
exit;
end;initialization
RegisterPlugin(‘xxx/hydra/plugins/PluginImplementation’, nil, TPluginImplementationWrapper);
end.
But if i want to import the functions above i get an exception that PluginImplementation cant be found.
Edit Exception of described issue :
I tried a few times to find the issue but it seems it raises only 9 of 10 cases. One time it worked.
The Result looks like this :
unit xxx_hydra_plugins_Import;
interface
uses
uHYCrossPlatformInterfaces,
uHYJavaTypes,
uHYJavaNonVisualPluginWrapper;type
{ Forward declarations }
IPluginInterface = interface;
TPluginImplementationWrapper = class;IPluginInterface = interface(IHYCrossPlatformNonVisualPlugin)
[‘{07FE71D7-11B4-442B-A571-FB4B1D6426C2}’]
function GetBoolean: JBoolean;
function GetDouble: JDouble;
function GetString: UnicodeString;
function GetShort: JShort;
function GetChar: JChar;
function GetInt: JInt;
function GetFloat: JFloat;
function GetByte: JByte;
function GetLong: JLong;
end;TPluginImplementationWrapper = class(THYJavaNonVisualPluginWrapper, IPluginInterface)
public
function GetBoolean: JBoolean;
function GetDouble: JDouble;
function GetString: UnicodeString;
function GetShort: JShort;
function GetChar: JChar;
function GetInt: JInt;
function GetFloat: JFloat;
function GetByte: JByte;
function GetLong: JLong;
end;implementation
uses
uHYCrossPlatformModuleController;function TPluginImplementationWrapper.GetBoolean: JBoolean;
var
lmethodId: JMethodID;
largs: JValueDynArray;
begin
lmethodId := Self.JVM.Reflection.GetMethodId(Self.ClassID, ‘GetBoolean’, ‘()Z’);
System.SetLength(largs, 0);
result := Self.JVM.Reflection.CallBooleanMethod(Self.InstanceID, lmethodId, largs);
exit;
end;function TPluginImplementationWrapper.GetDouble: JDouble;
var
lmethodId: JMethodID;
largs: JValueDynArray;
begin
lmethodId := Self.JVM.Reflection.GetMethodId(Self.ClassID, ‘GetDouble’, ‘()D’);
System.SetLength(largs, 0);
result := Self.JVM.Reflection.CallDoubleMethod(Self.InstanceID, lmethodId, largs);
exit;
end;function TPluginImplementationWrapper.GetString: UnicodeString;
var
lmethodId: JMethodID;
largs: JValueDynArray;
begin
lmethodId := Self.JVM.Reflection.GetMethodId(Self.ClassID, ‘GetString’, ‘()Ljava/lang/String;’);
System.SetLength(largs, 0);
result := Self.JVM.ValueConverter.ConvertToString(Self.JVM.Reflection.CallObjectMethod(Self.InstanceID, lmethodId, largs));
exit;
end;function TPluginImplementationWrapper.GetShort: JShort;
var
lmethodId: JMethodID;
largs: JValueDynArray;
begin
lmethodId := Self.JVM.Reflection.GetMethodId(Self.ClassID, ‘GetShort’, ‘()S’);
System.SetLength(largs, 0);
result := Self.JVM.Reflection.CallShortMethod(Self.InstanceID, lmethodId, largs);
exit;
end;function TPluginImplementationWrapper.GetChar: JChar;
var
lmethodId: JMethodID;
largs: JValueDynArray;
begin
lmethodId := Self.JVM.Reflection.GetMethodId(Self.ClassID, ‘GetChar’, ‘()C’);
System.SetLength(largs, 0);
result := Self.JVM.Reflection.CallCharMethod(Self.InstanceID, lmethodId, largs);
exit;
end;function TPluginImplementationWrapper.GetInt: JInt;
var
lmethodId: JMethodID;
largs: JValueDynArray;
begin
lmethodId := Self.JVM.Reflection.GetMethodId(Self.ClassID, ‘GetInt’, ‘()I’);
System.SetLength(largs, 0);
result := Self.JVM.Reflection.CallIntMethod(Self.InstanceID, lmethodId, largs);
exit;
end;function TPluginImplementationWrapper.GetFloat: JFloat;
var
lmethodId: JMethodID;
largs: JValueDynArray;
begin
lmethodId := Self.JVM.Reflection.GetMethodId(Self.ClassID, ‘GetFloat’, ‘()F’);
System.SetLength(largs, 0);
result := Self.JVM.Reflection.CallFloatMethod(Self.InstanceID, lmethodId, largs);
exit;
end;function TPluginImplementationWrapper.GetByte: JByte;
var
lmethodId: JMethodID;
largs: JValueDynArray;
begin
lmethodId := Self.JVM.Reflection.GetMethodId(Self.ClassID, ‘GetByte’, ‘()B’);
System.SetLength(largs, 0);
result := Self.JVM.Reflection.CallByteMethod(Self.InstanceID, lmethodId, largs);
exit;
end;function TPluginImplementationWrapper.GetLong: JLong;
var
lmethodId: JMethodID;
largs: JValueDynArray;
begin
lmethodId := Self.JVM.Reflection.GetMethodId(Self.ClassID, ‘GetLong’, ‘()J’);
System.SetLength(largs, 0);
result := Self.JVM.Reflection.CallLongMethod(Self.InstanceID, lmethodId, largs);
exit;
end;initialization
RegisterPlugin(‘xxx/hydra/plugins/PluginImplementation’, nil, TPluginImplementationWrapper);
end.
Since this topic is just for education purpose for now until i can display workable results i wrote a simple application in
32-Bit in Delphi.
My Java Modulemanager i have written is a bit bigger to show, but the required snippet for setup looks as followed:
FModuleManager := THYModuleManager.Create(nil);
FModuleManager.AutoLoad := False;
FModuleManager.ResolveInterfacesToOwner := True;
FModuleManager.EnforceSecurity := False;
FModuleManager.LoadJavaModule(AFilePath); // leads to xxx.hydra.plugins.jar
FModuleManager.CreateNonVisualPlugin(APluginName, hPlugin); // Pluginname is
‘NonVisualPlugin’ and hPlugin is IHYNonVisualPlugin
The last Part is that i run the methods through a button that should call the values from java and write them to a memo field.
procedure TMainFrm.btn_java_performClick(Sender: TObject);
var
hText: string;
hRawPlugin: IHYNonVisualPlugin;
hPlugin: IPluginInterface;
hIndex: Integer;
hValue: Integer;
begin
hRawPlugin := FJavaModule.LoadNonVisualPlugin(te_path.Text, te_plugin.Text, hIndex);if (Supports(hRawPlugin, IPluginInterface)) then
begin
hPlugin := hRawPlugin as IPluginInterface;
if (hPlugin <> nil) then
begin
me_javatext.Text := ‘’;me_javatext.Lines.Add('GetBoolean(): ' + Ifthen(hPlugin.GetBoolean(), 'True', 'False')); me_javatext.Lines.Add('GetDouble(): ' + FloatToStr(hPlugin.GetDouble())); hText := string.copy(hPlugin.GetString()); me_javatext.Lines.Add('GetString(): ' + hText); hValue := hPlugin.GetInt(); me_javatext.Lines.Add('GetInt(): ' + IntToStr(hValue)); me_javatext.Lines.Add('GetFloat(): ' + FloatToStr(hPlugin.GetFloat())); me_javatext.Lines.Add('GetByte(): ' + IntToStr(hPlugin.GetByte())); me_javatext.Lines.Add('GetLong(): ' + FloatToStr(hPlugin.GetLong())); end;
end;
FJavaModule.UnloadPlugin(hRawPlugin);
hPlugin := nil;
end;
in this case following functions dont work :
function GetShort: JShort; -> Access Violation in module jvm.dll function GetChar: JChar; -> Access Violation in module jvm.dll function GetInt: JInt; -> Access Violation in module jvm.dll function GetFloat: JFloat; -> Access Violation in module jvm.dll function GetByte: JByte; -> Access Violation in module jvm.dll function GetLong: JLong; -> Access Violation in module jvm.dll
I had this phenomen before with GetString and GetInt. Sometimes GetInt worked flawless but GetString had this exception… a few renewals of
interface generator then seemed to switch the situation and getInt worked but GetString ended up into an exception.
Also as open question is how i can add Interfaces to my structure like following:
Plugin
→ HeaderInterface
→ Name : string
→ ID : Integer
→ BodyInterface
→ Name: String;
→ ID: Integer;
In case its required i can send the full projects privately as attachment in order that it can be evaluated. But as far i see it, this are fundamental basics. I cant design or handle more complex cases when the described basic cant be used. And i can´t ask my company for invest into hydra and\or other remobject products if it wont help us in our further development. I´m at end of ideas.