Calling JavaVM from Pascal lazarus / 66-Unix ---------------------------------------------------------------------- BertVerhees · 2016-11-01 17:15 Hi, please help me. I already found a lot of information. But I need one more step. I think I am not very smart to understand how this can be done. Maybe it is a piece of cake for an experienced FPC programmer (I hope so) When you look at this URL: http://www.pacifier.com/~mmead/jni/delphi/JEDI/DOCS/delphi- jni-3.html#Example The problem is how to create the JavaVM object. It does not exist in FPC library (JNI.pas), this URL is Delphi code. In Delphi it is done like this:       // Create the JVM (using a wrapper class)     JavaVM := TJavaVM.Create; The rest of the code is very understandable, and very similar between Delphi, FPC, and also C-library which comes with Java (from which it is extracted) So, please help me how to start the JVM, and I think I am perfectly happy. Important to notice is that I am working in Linux. As thank, I can write a wiki how to use Java-code from a Delphi application. because that is what I intent to do. Thanks very much. ---------------------------------------------------------------------- Leledumbo · 2016-11-01 18:49 FPC's jni unit is a translation of Java's original jni header. The one in that article has an additional wrapper, whose link is provided in the quick start part. No idea if that wrapper is FPC compatible or not, you should figure it out yourself or ask the author. Using FPC jni unit, the manipulation is highly similar to how it's done in C. ---------------------------------------------------------------------- Thaddy · 2016-11-02 08:32 First: the features in FPC are rather better than that particular component. Like the pas2jni utility or even the PPCJVM to handle java code. Second: I used that particular component in a professional settings a couple of years ago with D2006. I had to fix a lot of bugs in it, though. The main one was jvm versioning. That still hasn't been fixed on that website so that code can never work with a modern jvm. Since FPC in Delphi mode is (very close to) fully compatible with D2006 I suggest you use it in Delphi mode. I don't expect you to have any problems because of the above to compile. If it still works is another matter. Expect problems with current Java/JVM  versions with that particular code. Also under modern Delphi versions for the same reason. I know that code you linked to is clear, but it is not complete and very old. It simply compiles with d2006 and fpc in delphi mode, but only works with very old jvm's because of said bug. You don't want that! If you explain what you want to do in a more specific manner we might be able to help. (I also don't see what Leledumbo did wrong.) I can't help you with my corrected code because it is only at binckbank, my former employer. ---------------------------------------------------------------------- Leledumbo · 2016-11-02 09:42 Quote from: BertVerhees on November 02, 2016, 08:46:50 amDear Leledumbo, I don't know what the purpose is of your answer. If you want to help me, please do, if not, don't, but then you don't need to tell me that. So, if others can help me with this question, I am grateful, and maybe I can help you too, in another case. Thanks Bert I guess old habit does die hard. Feel free to wait for our customer service ---------------------------------------------------------------------- BertVerhees · 2016-11-02 14:58 Leledumbo, Keep on trying  Thaddy, I found a better implementation of JNI: https://sites.google.com/site/aminer68/jni-wrapper-for-delphi-and- freepascal It has version 2.85, and the according text is written in March 2016, but it is written for Windows. But I had no problems skipping/removing all the Windows related stuff (what Lazarus did not remove), but my work still fails I did it like this: procedure TJavaRuntime.Initialize;   begin    if DLLHandle <> 0 then       exit; // already initialized.     {$IFDEF FPC}     FRuntimeLib := '/usr/lib/jvm/java-8-oracle/jre/lib/amd64/server/libjvm.so';     DLLHandle := LoadLibrary(PChar(FRuntimeLib));    {$ELSE}     DLLHandle := SafeLoadLibrary(FRuntimeLib);    {$endif}     if DLLHandle = 0 then       raise EJavaRuntimeCreation.Create('Could not load DLL ' + FRuntimeLib);     @CreateVM := getProcAddress(DLLHandle, 'JNI_CreateJavaVM');     @GetDefaultArgs := getProcAddress(DLLHandle, 'JNI_GetDefaultJavaVMInitArgs');     @GetCreatedVMs := getProcAddress(DLLHandle, 'JNI_GetCreatedJavaVMs');     if (@CreateVM = Nil) or (@GetDefaultArgs = Nil) or (@GetCreatedVMs = Nil) then       raise EJavaRuntimeCreation.Create('Dynamic Link Library ' + FRuntimeLib + ' is not valid.');     vmargs2.version := $00010008; GetDefaultArgs(@vmargs2);   end; You see, I load the jvm-library as needed in Linux. DLLHandle become non-zero, so I think it loads it (if I create a spelling-error in the library-name, DLLHandle remains zero.) The finding of the procadresses seems to fail. @CreateVM: > Some background on CreateVM: CreateVM : TCreateVM; TCreateVM = function (vm : PPJavaVM ; penv : PPJNIEnv ; p : Pointer) : jint; {$IFDEF WIN32} stdcall; {$ENDIF} {$IFDEF LINUX} cdecl; {$ENDIF} (by the way, cdecl is active, stdcall is grayed out in the Lazarus editor) I try to call one, like this in function TJavaRuntime.GetVM Like here if CreateVM(@pvm, @penv, args) <>0 then       raise EJavaRuntimeCreation.Create('Could not create JVM'); It does not go to the exception, but raises another Exception: Project jtest1 raised exception class 'External: SIGSEGV'.  At address 7FFFE62582B4 I read somewhere that SIGSEGV is entering an memory location which is  a fault of illegal memory access. That brings me back to the code where the address of CreateVM is retrieved in this way @CreateVM := getProcAddress(DLLHandle, 'JNI_CreateJavaVM'); I am sure that a function with that name exist in that library: ~ > nm -D /usr/lib/jvm/java-8-oracle/jre/lib/amd64/server/libjvm.so fragment 000000000070e0f0 T jio_snprintf 000000000070de80 T jio_vfprintf 000000000070e0d0 T jio_vsnprintf 00000000006d0880 T JNI_CreateJavaVM 00000000006cd1d0 T JNI_GetCreatedJavaVMs 00000000006cd210 T JNI_GetDefaultJavaVMInitArgs 000000000070ee00 T JVM_Accept 0000000000713990 T JVM_ActiveProcessorCount 0000000000715a20 T JVM_AllocateNewArray 0000000000727340 T JVM_AllocateNewObj So the call with getProcAddress(.....) does not lead to an valid address. It can be the case that the LoadLibrary(PChar(FRuntimeLib)) function does not load the library well, allthough it has a valid handle. (I also tried SafeLoadLibrary(FRuntimeLib); Or the getProcAddress(.....) has some problems. (By the way, I also tried GetProcedureAddress). I also tried other jvm-libraries, so that cannot be the problem. Who knows what is going wrong here? Thanks in advance Best regards Bert Verhees ---------------------------------------------------------------------- Thaddy · 2016-11-03 07:59 There is a huge bug where it says: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i {$IFDEF FPC}    FRuntimeLib := '/usr/lib/jvm/java-8-oracle/jre/lib/amd64/server/libjvm.so';    DLLHandle := LoadLibrary(PChar(FRuntimeLib));   {$ELSE}  Why? Because you are using the code for windows with fpc, but here you define a linux path. fpc is always defined for all versions of fpc, because it is fpc That code should at the minimum be: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i {$IFDEF UNIX}  // not $ifdef fpc    FRuntimeLib := '/usr/lib/jvm/java-8-oracle/jre/lib/amd64/server/libjvm.so';    DLLHandle := LoadLibrary(PChar(FRuntimeLib));   {$ELSE} That works probably better for you but is still not valid for all platforms. Just nix and windows. That's a first glance. Also get rid of the PChar cast. That means that class field is defined wrong. There's probably more to fix, but this one is a huge bug and may help you further. Also: don't use a full path. Both under nixes and windows and  properly installed java, you can simply use just the library name without .so or .dll and the path. The full path is useful for debugging, though. There may be is also an issue with the procedure names for getprocaddress. These are case sensitive. even under windows. So don't Pascallify/ProperCase them.Check those names. Although I see you tried to do that. ---------------------------------------------------------------------- BertVerhees · 2016-11-03 08:50 Thank you Thaddy, for your improvements. You are right. But thanks to the excellent quality of the Lazarus code-editor, the non-active code-path is grayed out. And it can also be checked by step by step debugging. Regarding to the librarypath, you are right too, but the library is loaded well, the library-handle has a value, and I tested it, when having a wrong spelling in the path, the handle becomes zero. The PChar casting, is needed by dynlibs. That maybe wrong or not. I do not have strong opinion on that. I also tried removing that by using the SafeLoadLibrary, same effect. I will use your tips when I have it working. The problem must be, I think, in the requesting for the function- address. But I don't know what the problem is. I am spending more than a day on this. ---------------------------------------------------------------------- marcov · 2016-11-03 09:25 Maybe stracing the resulting binary gives some information about what goes wrong? ---------------------------------------------------------------------- BertVerhees · 2016-11-03 09:51 That can be a good idea, but I am not able to get any wiser from a strace-output. I have attached it. Hopefully someone can give a clue? If you need more info, I will happily provide it. Bert ---------------------------------------------------------------------- marcov · 2016-11-03 14:49 There are two sigsegv's. The first internally in Java (?!?!) Code: [Select]open("/usr/lib/jvm/java-8-oracle/jre/lib/meta-index", O_RDONLY) = 3 fstat(3, {st_mode=S_IFREG|0755, st_size=2034, ...}) = 0 read(3, "% VERSION 2\n% WARNING: this file"..., 4096) = 2034 read(3, "", 4096)                       = 0 close(3)                                = 0 mmap(NULL, 251658240, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0) = 0x7f73f7c8c000 mmap(0x7f73f7c8c000, 2555904, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f73f7c8c000 mmap(NULL, 3932160, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0) = 0x7f73f78cc000 mmap(0x7f73f78cc000, 40960, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f73f78cc000 --- SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_MAPERR, si_addr=0} --- rt_sigreturn({mask=[QUIT]})             = 6 futex(0x8efc54, FUTEX_WAIT_BITSET_PRIVATE, 1, {10757, 945339826}, ffffffff) = -1 ETIMEDOUT (Connection timed out) futex(0x8efc28, FUTEX_WAKE_PRIVATE, 1)  = 0 futex(0x8efc54, FUTEX_WAIT_BITSET_PRIVATE, 1, {10757, 946451175}, ffffffff) = -1 ETIMEDOUT (Connection timed out) futex(0x8efc28, FUTEX_WAKE_PRIVATE, 1)  = 0 futex(0x8efc54, FUTEX_WAIT_BITSET_PRIVATE, 1, {10757, 947539238}, ffffffff) = -1 ETIMEDOUT (Connection timed out) futex(0x8efc28, FUTEX_WAKE_PRIVATE, 1)  = 0 mmap(0x6c9c00000, 4131389440, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0) = 0x6c9c00000 mmap(NULL, 8073216, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0) = 0x7f73f7119000 mmap(0x7f73f78cb000, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f73f78cb000 The later one is possibly during a very prolongued loading of Code: [Select]open("/usr/lib/jvm/java-8-oracle/jre/lib/rt.jar", O_RDONLY) = 3 But it could be that that is after RT has been mostly initialized. Note that crashing doesn't necessarily mean that the geprocedureaddress goes wrong, it can be the function signature or the parameters too. But I don't work with Java, so that is beyond me. ---------------------------------------------------------------------- Thaddy · 2016-11-03 15:04 The mmap suggests also maybe an alignment issue. Because that comes maybe from fpc's memorymanager? Try to add {$PACKRECORDS C} if it is not there. I have time to test but not to rewrite So code is more welcome than straces. Where are you now? Codewise? I do quite a lot with Java. ---------------------------------------------------------------------- BertVerhees · 2016-11-03 15:57 Hi Marco, thanks for your research. (Let me introduce a little-bit about myself: I am quite good at java, and also, I am quite good at Delphi (I even worked for the Borland helpdesk a long time ago). But I do not have much knowledge of FPC-libraries. And I was reasonable good in C. I used to work with it, a long time ago. I can still read it, but I make many errors when I program in it, so I don't do that anymore.) But I found yesterday a Youtube instruction about how to connect to libjvm, it had code in C. And it worked. So there cannot be a problem with the library itself. You see in bold (below) how the call needs to be done in C. I must regret to say, that I am not able to translate that with certainty to Pascal. We must say that the Pascal JNI code works fine in Windows, but maybe the Java-dll is more tolerant against errors? (The windows counterpart is called javai.dll) I can create tomorrow a very simple small testfile which should do the job. Say, a translation of the C-program as I think it should be translated (which is, like I said, not necessary right) Here is the code, that works, maybe you can indicate something from it: #include #include #include JNIEnv* create_jvm(JavaVM **java) {    JNIEnv *env;    JavaVMInitArgs args;    JavaVMOption options;    int ret;    args.version = JNI_VERSION_1_6;    args.nOptions = 1;    args.options = &options;    args.ignoreUnrecognized = 0;    options.optionString = "-Djava.class.path=./";    ret = JNI_CreateJavaVM(java, (void**)&env, &args);     if(ret < 0 || !env)       printf("unable to launch JVM %d\n", ret);    else       printf("Launched JVM\n");    return env; } int main(int argc, char *argv[]) {    JavaVM *jvm;    JNIEnv *env;    jclass myclass;    jmethodID main_method;    jmethodID addnumber_method;    jint a_num = 5;    jint b_num = 3;    env = create_jvm(&jvm);    if(env == NULL)       exit(1);    myclass = (*env)->FindClass(env, "myjava");    main_method = (*env)->GetStaticMethodID(env, myclass, "main", "([Ljava/lang/String;)V");    addnumber_method = (*env)->GetStaticMethodID(env, myclass, "add_numbers", "(II)I");    (*env)->CallStaticVoidMethod(env, myclass, main_method, NULL);    printf("sum = %d\n", (*env)->CallStaticIntMethod(env, myclass, addnumber_method, a_num, b_num));    return 0; } This is the small Java Class which is used for test in the C-code public class myjava{    public static void main(String[] args){       System.out.println("C calling java");    }    public static int add_numbers(int a, int b){       int sum = a + b;       return sum;    } } ---------------------------------------------------------------------- Cyrax · 2016-11-03 16:32 Perhaps your problems stir from calling convention. Windows generally uses stdcall, other OS environments cdecl calling convention. ---------------------------------------------------------------------- BertVerhees · 2016-11-03 16:36 Thanks for your suggestion. It must be hard to read everything. So, let me tell you, I do cdecl. See my message from: November 02, 2016, 03:58:28 pm ---------------------------------------------------------------------- BertVerhees · 2016-11-03 17:02 Here is my very short Pascal program which does nothing then initializing the JVM, and crashes while doing that And you need my JNI.pas, which I attach. Maybe it is the same as the one with FPC, but I am not sure about that. program project1; {$MODE Delphi} uses   {$IFDEF UNIX}{$IFDEF UseCThreads}   cthreads,   {$ENDIF}{$ENDIF}   Classes, JNI, dynLibs   { you can add units after this }; type   TCreateVM = function (vm : PPJavaVM ; penv : PPJNIEnv ; p : Pointer) : jint; cdecl; var   runtimeLib: AnsiString;   libHandle : THandle;   CreateVM : TCreateVM;   PVM : PJavaVM;   penv : PJNIEnv;   args : Pointer; begin   runtimeLib := '/usr/lib/jvm/java-8-oracle/jre/lib/amd64/server/libjvm.so';   libHandle :=  LoadLibrary(PChar(runtimeLib));   @CreateVM := getProcAddress(libHandle, 'JNI_CreateJavaVM');   CreateVM(@pvm, @penv, args); end. ---------------------------------------------------------------------- molly · 2016-11-03 17:12 Code: [Select]var   runtimeLib: AnsiString;   libHandle : THandle;   CreateVM : TCreateVM;   PVM : PPJavaVM;   penv : PPJNIEnv;   args : Pointer; begin   runtimeLib := '/usr/lib/jvm/java-8-oracle/jre/lib/amd64/server/libjvm.so';   libHandle :=  LoadLibrary(PChar(runtimeLib));   @CreateVM := getProcAddress(libHandle, 'JNI_CreateJavaVM');   CreateVM(pvm, penv, args); end. ---------------------------------------------------------------------- BertVerhees · 2016-11-03 17:26 It still crashes on my computer, molly, it doesn't on yours? ---------------------------------------------------------------------- molly · 2016-11-03 17:33 In case you are sure it crashes: make sure the arguments point to something valid ? BTW: i know it sounds stupid but, check each and every line for the result so that you are able to check what is actually happening. I am assuming the loadproc went ok, but you have no way of telling. The reason i let you changed the pointers is that it is more important to understand what is happening first before applying tricks like that (the link of the wrapper you posted in your first post did some pretty weird stuff like checking if the address of a local variable = nil, which is actually pointless). Make sure that you provide the right user class directory in case providing that to args. ---------------------------------------------------------------------- BertVerhees · 2016-11-04 07:56 molly, I am sure that the loadlibrary works, because it returns a valid handle, although, it returns a non-zero handle, which it does not if it cannot find the library. There is also no reason to doubt it could not load the library. It is a valid library, like many others, and it is well tested (see my C-program I published yesterday in this discussion). I think we can trust that step. So the problem is in the getProcAddress and the parameters it gets. I used to be quite good in pointers to pointers, and so, but not anymore. For over fifteen or twenty years, I have worked with programming environments which hide these kind of things. So I lost my talent to solve these kinds of things. And I am not sad about that. That is why I am writing here. I was hoping that this kind of question would be a piece of cake. I was hoping that there were tons of example-programs which load unix-libraries, from which we can learn. But it seems to turn out that I have bad luck. You say that the code I pointed to was wacky, checking if a pointer is nil? I think the writers are used to OO thinking, where not initialized objects are nil? But their code works on Windows. Maybe Windows is more permissive? The JNI layer would be very useful to me. It would give me the opportunity to have really fine JVM-libraries available for FPC. This could be very useful, also for the FPC/Lazarus community. Not for using every micro-second., it would be too slow, but for transferring complete jobs/algorithms to the "other" side. Why would one want to do that? For Java (and other JVM languages) have lots of academic use, there is so much knowledge available and for free, while FPC has it strong points too. It is fast, and easy to use as a RAD environment and it hides platform dependencies in code. Both are a strong pair, which give lots of opportunities. The academic knowledge which is available in JVM languages, one can always rewrite that in FPC. But that feels like reinventing the wheel, but then in an uneasy way. There are other ways too to connect to a Java-environment, for example, a small internal REST mechanism. The boilerplate to do that can be generated, also from the Pascal side. I think that can work too. But I kind of hate that solution a bit. It feels too much as not being connected, and modules living apart on a network connection. But it also has advantages in context of scalability. So, I still hope, someone is able to solve this problem, which I cannot solve, and if not, I am already preparing plan B. I have to. My product needs it. ---------------------------------------------------------------------- Thaddy · 2016-11-04 08:14 From your example code I deduce that you really mean to call out from java to a Pascal library? Then simply write that library in FPC and the Pas2JNI utility will create the JNI wrappers for you. I already pointed to that solution. If my deduction is right I can post a small example sometime today. See also http://wiki.freepascal.org/pas2jni that already contains examples. ---------------------------------------------------------------------- BertVerhees · 2016-11-04 08:25 Thanks Thaddy, but no, that is not what I am looking for. That side of JNI is much better documented, I have noticed. I like to use JVM- libraries within a FPC program. Best regards Bert ---------------------------------------------------------------------- BertVerhees · 2016-11-07 10:51 I wonder how Android programming is handled with FPC? There must be a lot of Java-interfacing. Or is it a matter of reinventing all available Java libraries on an Android-device? See this architecture overview: https://www.tutorialspoint.com/android/android_architecture.htm The second layer (application framework) is full Java, the third layer is partly Java libraries and partly C/C++ But if it is the first choice, then must there be a lot of example code how to load the JVM, and call some function in a class. What would be a good place to look for some example code? ---------------------------------------------------------------------- marcov · 2016-11-07 11:06 Quote from: BertVerhees on November 03, 2016, 06:02:21 pmvar ....   args : Pointer; begin   runtimeLib := '/usr/lib/jvm/java-8-oracle/jre/lib/amd64/server/libjvm.so';   libHandle :=  LoadLibrary(PChar(runtimeLib));   @CreateVM := getProcAddress(libHandle, 'JNI_CreateJavaVM');   CreateVM(@pvm, @penv, args); end.          As said, I'm a Java non, but it is a bit strange to pass an uninitialized pointer by value. At least initialize it to zero. Other things I can imagine (since you seem to use 64-bit Linux) is alignment issues in the JNI header translation. That would be tested by printing the sizeof() of structures both in FPC and in a C program and comparing them ---------------------------------------------------------------------- BertVerhees · 2016-11-07 11:14 Thanks very much, I will try it later. I am on another computer for a few days. Looks interesting. The "args" however, should have content. It is used to pass options to the JVM, for example, which Java-version should the JVM start, the class-path.     JavaVMInitArgs vm_args; /* JDK/JRE 6 VM initialization arguments */     JavaVMOption* options = new JavaVMOption[1];     options[0].optionString = "-Djava.class.path=/usr/lib/java";     vm_args.version = JNI_VERSION_1_6;     vm_args.nOptions = 1;     vm_args.options = options;     vm_args.ignoreUnrecognized = false; JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args); http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/invocat ion.html ---------------------------------------------------------------------- Thaddy · 2016-11-07 11:14 The problem is that both the projects that Bert started off with do both have a considerable amount of real bugs. Atm I don't have the time (At BinckBank I got over 2 whole weeks!) to fix this again, unless BinckBank provides me with the code I wrote back then. That's performant code (very performant) and safe to use. I will try to get it. ---------------------------------------------------------------------- BertVerhees · 2016-11-07 11:16 We'll see, anyway, I keep following this discussion, if you have something new, please notify also here. Bert ---------------------------------------------------------------------- molly · 2016-11-26 16:08 Quote from: BertVerhees on November 03, 2016, 06:26:30 pmIt still crashes on my computer, molly, it doesn't on yours? No, but i'm not using 64 bit. The reason why it fails (i get a runtime error 216 when using your example code) is that the argument pointer is not allowed to be nil. If you follow the rules that seem to apply for the JDK, then things start to work. I was lazy, so no interface  My Output: Code: [Select]libjvm dynamic loading succeeded proc JNI_CreateJavaVM() was located proc JNI_GetDefaultJavaVMInitArgs() was located Creating initargs succeeded creating VM succeeded going to sleep mode for a sec. closing down Unloading of library succeeded ---------------------------------------------------------------------- Thaddy · 2016-11-26 17:20 It is not difficult. It takes time.... A programmer should be able to know what is wrong and right and how to solve it, We all got a proper educations fgs. This is a matter of commercial software, written under pressure, with insufficient education. Or worse: insufficient commitment. It is also called a recipe for disaster. A professional would have finished it by now, ---------------------------------------------------------------------- BertVerhees · 2016-11-26 19:05 Thanks Molly, I will look at it in a few days. Bert ---------------------------------------------------------------------- molly · 2016-12-01 15:21 @Thaddy: I hear you. Research simply takes time so one should account for that. Especially when it concerns commercial projects. Can't be more obvious then that. It took me more time to install the whole lot then it took me to produce something workable. Examples are all out in the open (even though mostly oriented at c language) If you can't do the time then don't do the crime