I'm writing an instrumentation tool for Java applications using JVMTI. I've seen that JVMTI detects when an exception has been thrown and when has been caught according to http://docs.oracle.com/javase/7/docs/platform/jvmti/jvmti.html#Exception.
This document states for both events Exception and ExceptionCatch
The exception field identifies the thrown exception object.
although it does not indicate on how to compare them during the run (i.e. to compare an exception provided in Exception corresponds to the expcetion caught in ExceptionCatch). In other words, for
# java -version
java version "1.7.0_85"
OpenJDK Runtime Environment (IcedTea 2.6.1) (7u85-2.6.1-5ubuntu0.14.04.1)
OpenJDK 64-Bit Server VM (build 24.85-b03, mixed mode)
it does not seem to be always true when comparing the jexception/jobject directly. Consider the JVMTI agent (source code below) that monitors the Exception and ExceptionCatch events, also consider the subsequent Java naïve example (source-code also included) that throws an Exception, and finally you can run the example with the agent through "make run" with the given Makefile (included at the very end).
If I run the example using OpenJDK 7 it gives me the followign results:
# make run
...
cb_Exception (exception=0x2ae6b8087be8)
cb_ExceptionCatch (exception=0x2ae6b80859f8 vs last_exception=0x2ae6b8087be8)
AreSameObject? = 0
cb_Exception (exception=0x2ae6b80859f8)
cb_ExceptionCatch (exception=0x2ae6b807a388 vs last_exception=0x2ae6b80859f8)
AreSameObject? = 0
cb_Exception (exception=0x2ae6b807a388)
cb_ExceptionCatch (exception=0x2ae6b807a388 vs last_exception=0x2ae6b807a388)
AreSameObject? = 1
cb_Exception (exception=0x2ae6b807a388)
cb_ExceptionCatch (exception=0x2ae6b8078108 vs last_exception=0x2ae6b807a388)
AreSameObject? = 0
cb_Exception (exception=0x2ae6b8078108)
cb_ExceptionCatch (exception=0x2ae6b8078108 vs last_exception=0x2ae6b8078108)
AreSameObject? = 1
cb_Exception (exception=0x2ae6b8078108)
cb_ExceptionCatch (exception=0x2ae6b8078108 vs last_exception=0x2ae6b8078108)
AreSameObject? = 1
before doing work
cb_Exception (exception=0x2ae6b8078108)
cb_ExceptionCatch (exception=0x2ae6b8078108 vs last_exception=0x2ae6b8078108)
AreSameObject? = 1
after doing work
If I run the example using IBM's Java 1.7.0 the situation is somewhat similar.
# make run
...
cb_Exception (exception=0x7d78a0)
cb_ExceptionCatch (exception=0x7d7950 vs last_exception=0x7d78a0)
AreSameObject? = 1
cb_Exception (exception=0x7d7938)
cb_ExceptionCatch (exception=0x7d7950 vs last_exception=0x7d7938)
AreSameObject? = 0
cb_Exception (exception=0x7d7938)
cb_ExceptionCatch (exception=0x7d79a8 vs last_exception=0x7d7938)
AreSameObject? = 1
before doing work
cb_Exception (exception=0x7d7a60)
cb_ExceptionCatch (exception=0x7d7a98 vs last_exception=0x7d7a60)
AreSameObject? = 0
after doing work
In OpenJDK's, notice that each Exception has a corresponding ExceptionCatch, yet there are six Exceptions outside the main Java code which I suspect that come from the Java VM itself. Notice that the value for exception is not the same in every pair. After th 5th callback call, the value for exception seems to get constant. The situation is somewhat similar in IBM's Java but with less exceptions raised. As a suggestion from Chen Harel from the comments, I've stored the raised exception and compared to the caught exception through the IsSameObject JNI's method but JNI claims that the exceptions are not the same.
So, can exceptions be identified during the application life-time by the Exception and ExceptionCatch? If so, which is the appropriate way to compare exceptions through JVMTI or JNI?
agent.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <jni.h>
#include <jvmti.h>
#define CHECK_JVMTI_ERROR(x,call) \
{ if (x != JVMTI_ERROR_NONE) { fprintf (stderr, "Error during %s in %s:%d\n", #call, __FILE__, __LINE__); } }
/* Global static data */
static jvmtiEnv *jvmti;
static jrawMonitorID ExtraeJ_AgentLock;
jobject last_exception;
static void JNICALL cb_Exception (jvmtiEnv *jvmti_env, JNIEnv* jni_env,
jthread thread, jmethodID method, jlocation location, jobject exception,
jmethodID catch_method, jlocation catch_location)
{
printf ("cb_Exception (exception=%p)\n", exception);
last_exception = exception;
}
static void JNICALL cb_ExceptionCatch (jvmtiEnv *jvmti_env,
JNIEnv* jni_env, jthread thread, jmethodID method, jlocation location,
jobject exception)
{
printf ("cb_ExceptionCatch (exception=%p vs last_exception=%p)\n"
"AreSameObject? = %d\n",
exception,
last_exception,
(*jni_env)->IsSameObject(jni_env, exception, last_exception));
}
JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *vm, char *options, void *reserved)
{
jint rc;
jvmtiError r;
jvmtiCapabilities capabilities;
jvmtiEventCallbacks callbacks;
/* Get JVMTI environment */
rc = (*vm)->GetEnv(vm, (void **)&jvmti, JVMTI_VERSION);
if (rc != JNI_OK)
{
fprintf (stderr, "Error!: Unable to create jvmtiEnv, rc=%d\n", rc);
return -1;
}
/* Get/Add JVMTI capabilities */
memset(&capabilities, 0, sizeof(capabilities));
capabilities.can_generate_exception_events = 1;
r = (*jvmti)->AddCapabilities(jvmti, &capabilities);
CHECK_JVMTI_ERROR(r, AddCapabilities);
/* Set callbacks and enable event notifications */
memset(&callbacks, 0, sizeof(callbacks));
callbacks.Exception = &cb_Exception;
callbacks.ExceptionCatch = &cb_ExceptionCatch;
r = (*jvmti)->SetEventCallbacks(jvmti, &callbacks, sizeof(callbacks));
CHECK_JVMTI_ERROR(r, SetEventCallbacks);
/* Exception events */
r = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE,
JVMTI_EVENT_EXCEPTION, NULL);
CHECK_JVMTI_ERROR(r, SetEventNotificationMode);
r = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE,
JVMTI_EVENT_EXCEPTION_CATCH, NULL);
CHECK_JVMTI_ERROR(r, SetEventNotificationMode);
return 0;
}
JNIEXPORT void JNICALL Agent_OnUnload(JavaVM *vm)
{
}
example.java
public class example
{
void except () throws Exception
{ throw new Exception ("new-exception"); }
void do_work()
{
System.out.println ("before doing work");
try
{ except(); }
catch (Exception e)
{}
System.out.println ("after doing work");
}
public static void main (String [] args)
{
example e = new example();
e.do_work ();
}
}
Makefile
JAVA_JDK=/usr/lib/jvm/java-7-openjdk-amd64
all: libagent.so example.class
libagent.so: agent.c
gcc -shared -fPIC -DPIC agent.c -o libagent.so -I$(JAVA_JDK)/include
example.class: example.java
javac example.java
run: libagent.so example.class
java -agentpath:$(PWD)/libagent.so example
UPDATE 9th, Nov
I have created a global reference to the exception as Chen Harel suggested. The modified version of the agent.c is as follows and the output of the execution is shown below.
agent.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <jni.h>
#include <jvmti.h>
#define CHECK_JVMTI_ERROR(x,call) \
{ if (x != JVMTI_ERROR_NONE) { fprintf (stderr, "Error during %s in %s:%d\n", #call, __FILE__, __LINE__); } }
/* Global static data */
static jvmtiEnv *jvmti;
static jrawMonitorID ExtraeJ_AgentLock;
jobject last_exception;
static void JNICALL cb_Exception (jvmtiEnv *jvmti_env, JNIEnv* jni_env,
jthread thread, jmethodID method, jlocation location, jobject exception,
jmethodID catch_method, jlocation catch_location)
{
printf ("cb_Exception (exception=%p)\n", exception);
last_exception = (*jni_env)->NewGlobalRef (jni_env, exception);
}
static void JNICALL cb_ExceptionCatch (jvmtiEnv *jvmti_env,
JNIEnv* jni_env, jthread thread, jmethodID method, jlocation location,
jobject exception)
{
printf ("cb_ExceptionCatch (exception=%p vs last_exception=%p)\n"
"AreSameObject? = %d\n",
exception,
last_exception,
(*jni_env)->IsSameObject(jni_env, exception, last_exception));
(*jni_env)->DeleteGlobalRef(jni_env, last_exception);
}
JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *vm, char *options, void *reserved)
{
jint rc;
jvmtiError r;
jvmtiCapabilities capabilities;
jvmtiEventCallbacks callbacks;
/* Get JVMTI environment */
rc = (*vm)->GetEnv(vm, (void **)&jvmti, JVMTI_VERSION);
if (rc != JNI_OK)
{
fprintf (stderr, "Error!: Unable to create jvmtiEnv, rc=%d\n", rc);
return -1;
}
/* Get/Add JVMTI capabilities */
memset(&capabilities, 0, sizeof(capabilities));
capabilities.can_generate_exception_events = 1;
r = (*jvmti)->AddCapabilities(jvmti, &capabilities);
CHECK_JVMTI_ERROR(r, AddCapabilities);
/* Set callbacks and enable event notifications */
memset(&callbacks, 0, sizeof(callbacks));
callbacks.Exception = &cb_Exception;
callbacks.ExceptionCatch = &cb_ExceptionCatch;
r = (*jvmti)->SetEventCallbacks(jvmti, &callbacks, sizeof(callbacks));
CHECK_JVMTI_ERROR(r, SetEventCallbacks);
/* Exception events */
r = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE,
JVMTI_EVENT_EXCEPTION, NULL);
CHECK_JVMTI_ERROR(r, SetEventNotificationMode);
r = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE,
JVMTI_EVENT_EXCEPTION_CATCH, NULL);
CHECK_JVMTI_ERROR(r, SetEventNotificationMode);
return 0;
}
JNIEXPORT void JNICALL Agent_OnUnload(JavaVM *vm)
{
}
** Output of make run **
# make run
...
cb_Exception (exception=0x2b13b0087c18)
cb_ExceptionCatch (exception=0x2b13b0085a08 vs last_exception=0x2b13e4001608)
AreSameObject? = 0
cb_Exception (exception=0x2b13b0085a08)
cb_ExceptionCatch (exception=0x2b13b007a388 vs last_exception=0x2b13e4001610)
AreSameObject? = 0
cb_Exception (exception=0x2b13b007a388)
cb_ExceptionCatch (exception=0x2b13b007a388 vs last_exception=0x2b13e4001618)
AreSameObject? = 1
cb_Exception (exception=0x2b13b007a388)
cb_ExceptionCatch (exception=0x2b13b0078108 vs last_exception=0x2b13b0085a00)
AreSameObject? = 0
cb_Exception (exception=0x2b13b0078108)
cb_ExceptionCatch (exception=0x2b13b0078108 vs last_exception=0x2b13b0085a08)
AreSameObject? = 1
cb_Exception (exception=0x2b13b0078108)
cb_ExceptionCatch (exception=0x2b13b0078108 vs last_exception=0x2b13b0085a10)
AreSameObject? = 1
before doing work
cb_Exception (exception=0x2b13b0078108)
cb_ExceptionCatch (exception=0x2b13b0078108 vs last_exception=0x2b13b0085a18)
AreSameObject? = 1
after doing work
jobject is a C++ pointer and the reference to the heap is handled beneath it. So this is more of a pointer to a pointer if you think about it.
The way to test if two jobjects are the same is to use the jni method IsSameObject
If you want to test the type of the exception, use GetObjectClass + IsInstanceOf
EDIT
Note that in order to keep jobject(s) valid between methods you will have to create a reference for them with jni NewGlobalRef method.
Related
I'll start with my code:
#include <jni.h>
int main(int argc, char const *argv[]) {
JavaVMInitArgs args;
JNIEnv *env;
JavaVM *vm;
args.version = JNI_VERSION_1_8;
args.ignoreUnrecognized = JNI_FALSE;
JNI_CreateJavaVM(&vm, (void **)&env, &args);
jclass System;
jclass PrintStream;
jobject out;
jfieldID outID;
jstring someText;
jmethodID println;
someText = (*env)->NewStringUTF(env, "Hello World");
System = (*env)->FindClass(env, "java/lang/System");
PrintStream = (*env)->FindClass(env, "java/io/PrintStream");
outID = (*env)->GetStaticFieldID(env, System, "out", "Ljava/io/PrintStream;");
out = (*env)->GetStaticObjectField(env, System, outID);
println = (*env)->GetMethodID(env, PrintStream, "println", "(Ljava/lang/String;)V");
(*env)->CallVoidMethod(env, out, println, someText);
return 0;
}
I would expect it to print "Hello World" but it does not, instead I get Segmentation fault (core dumped) annoying error. I could not figure out what is wrong with this code, I tried to comment out everything after someText = (*env)->NewStringUTF(env, "Hello World"); and the program didn't crash, I also tried to comment only the someText = (*env)->NewStringUTF(env, "Hello World"); and I worked too. I even changed the println signature to "boolean" and passed 0 to it, program printed "false" as I would expect so I guess this is something wrong with NewStringUTF method.
openjdk version "1.8.0_131"
OpenJDK Runtime Environment (build 1.8.0_131-b11)
OpenJDK 64-Bit Server VM (build 25.131-b11, mixed mode)
(gdb) bt
#0 0x00007ffff713b2ff in ?? () from /usr/lib/jvm/java-8-openjdk/jre/lib/amd64/server/libjvm.so
#1 0x00007ffff78955c4 in ?? () from /usr/lib/jvm/java-8-openjdk/jre/lib/amd64/server/libjvm.so
#2 0x00007ffff7507e71 in JNI_CreateJavaVM () from /usr/lib/jvm/java-8-openjdk/jre/lib/amd64/server/libjvm.so
#3 0x0000000000400558 in main (argc=1, argv=0x7fffffffe678) at main.c:11
(gdb) info f
Stack level 0, frame at 0x7fffffffe310:
rip = 0x7ffff713b2ff; saved rip = 0x7ffff78955c4
called by frame at 0x7fffffffe490
Arglist at 0x7fffffffe2c8, args:
Locals at 0x7fffffffe2c8, Previous frame's sp is 0x7fffffffe310
Saved registers:
rbx at 0x7fffffffe2d8, rbp at 0x7fffffffe300, r12 at 0x7fffffffe2e0, r13 at 0x7fffffffe2e8, r14 at 0x7fffffffe2f0,
r15 at 0x7fffffffe2f8, rip at 0x7fffffffe308
after commenting out the NewStringUTF
(gdb) bt
#0 0x00007fffe61082b4 in ?? ()
#1 0x0000000000000246 in ?? ()
#2 0x00007fffe6108160 in ?? ()
#3 0x00007fffffffe0f0 in ?? ()
#4 0x00007fffffffe090 in ?? ()
#5 0x00007ffff78f6748 in ?? () from /usr/lib/jvm/java-8-openjdk/jre/lib/amd64/server/libjvm.so
Backtrace stopped: previous frame inner to this frame (corrupt stack?)
(gdb) info f
Stack level 0, frame at 0x7fffffffde80:
rip = 0x7fffe61082b4; saved rip = 0x246
called by frame at 0x7fffffffde88
Arglist at 0x7fffffffde70, args:
Locals at 0x7fffffffde70, Previous frame's sp is 0x7fffffffde80
Saved registers:
rip at 0x7fffffffde78
After some observations, looks like the function JNI_CreateJavaVM is crashing after NewStringUTF was added, but it's "working" after it's removed. How weird is that?
This is JDK and JRE I am using: https://www.archlinux.org/packages/extra/x86_64/jdk8-openjdk/
I am compiling with this command:
gcc \
-I /usr/lib/jvm/java-8-openjdk/include/ \
-I /usr/lib/jvm/java-8-openjdk/include/linux/ \
-L /usr/lib/jvm/java-8-openjdk/jre/lib/amd64/server/ \
-l jvm \
main.c
And running the file with
export LD_LIBRARY_PATH=/usr/lib/jvm/java-8-openjdk/jre/lib/amd64/server/
./a.out
Next Day
Different code, same problem:
#include <jni.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char const *argv[]) {
JavaVMInitArgs args;
JavaVM *jvm;
JNIEnv *env;
args.version = JNI_VERSION_1_8;
args.ignoreUnrecognized = JNI_FALSE;
printf("%s\n", "Creating VM");
JNI_CreateJavaVM(&jvm, (void **)&env, &args);
printf("%s\n", "VM Was Created");
jclass String = (*env)->FindClass(env, "java/lang/String");
if (String == NULL) {
printf("%s\n", "String was NULL");
exit(1);
}
jmethodID method = (*env)->GetMethodID(env, String, "codePointAt", "(I)I");
if (method == NULL) {
printf("%s\n", "method was NULL");
exit(1);
}
printf("%s\n", "I am finishing here");
(*jvm)->DestroyJavaVM(jvm);
return 0;
}
Compile and run:
$ gcc \
> -I /usr/lib/jvm/java-8-openjdk/include/ \
> -I /usr/lib/jvm/java-8-openjdk/include/linux/ \
> -L /usr/lib/jvm/java-8-openjdk/jre/lib/amd64/server/ \
> -l jvm \
> main.c && ./a.out
Creating VM
Segmentation fault (core dumped)
But if I comment the part of the code:
#include <jni.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char const *argv[]) {
JavaVMInitArgs args;
JavaVM *jvm;
JNIEnv *env;
args.version = JNI_VERSION_1_8;
args.ignoreUnrecognized = JNI_FALSE;
printf("%s\n", "Creating VM");
JNI_CreateJavaVM(&jvm, (void **)&env, &args);
printf("%s\n", "VM Was Created");
jclass String = (*env)->FindClass(env, "java/lang/String");
if (String == NULL) {
printf("%s\n", "String was NULL");
exit(1);
}
/*jmethodID method = (*env)->GetMethodID(env, String, "codePointAt", "(I)I");
if (method == NULL) {
printf("%s\n", "method was NULL");
exit(1);
}*/
printf("%s\n", "I am finishing here");
(*jvm)->DestroyJavaVM(jvm);
return 0;
}
Compile and run:
$ gcc \
> -I /usr/lib/jvm/java-8-openjdk/include/ \
> -I /usr/lib/jvm/java-8-openjdk/include/linux/ \
> -L /usr/lib/jvm/java-8-openjdk/jre/lib/amd64/server/ \
> -l jvm \
> main.c && ./a.out
Creating VM
VM Was Created
I am finishing here
So, for me, it works as expected:
> gdb ./main
...
(gdb) run
Starting program: ..../issue/main
[New Thread 0x1403 of process 2600]
[New Thread 0x1503 of process 2600]
warning: unhandled dyld version (15)
Hello World
[Inferior 1 (process 2600) exited normally]
(gdb)
However, I have slightly different env. macOS and I use Oracle's JDK.
Maybe you can try installing debug info and check what exactly happens inside JDK?
This question already has answers here:
Calling C# method within a Java program
(2 answers)
Closed 7 years ago.
I need to call C# function from java and to this I have created the following.
I have a create a java header file Authenticator.h , here is the code:
#include <jni.h>
/* Header for class Authenticator */
#ifndef _Included_Authenticator
#define _Included_Authenticator
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: Authenticator
* Method: authenticate
* Signature: (Ljava/lang/String;Ljava/lang/String;)Z
*/
JNIEXPORT jboolean JNICALL Java_Authenticator_authenticate
(JNIEnv *, jobject, jstring, jstring);
#ifdef __cplusplus
}
#endif
#endif
I have then create a C# function that Authenticate
namespace SharpAuthenticator
{
public class Authenticator
{
public bool Authenticate(String username,String password)
{
return username == "user" && password == "login";
}
}
}
Then I am trying to call the C# function from C++(project to create a dll) using the code below;
String^ toString(const char *str)
{
int len = (int)strlen(str);
array<unsigned char>^ a = gcnew array<unsigned char>(len);
int i = 0;
while (i < len)
{
a[i] = str[i];
i++;
}
return Encoding::UTF8->GetString(a);
}
bool authenticate(const char *username, const char *password)
{
SharpAuthenticator::Authenticator::Authenticate(toString(username), toString(password));
}
JNIEXPORT jboolean JNICALL Java_Authenticator_authenticate
(JNIEnv *env, jobject c, jstring name, jstring pass)
{
jboolean result;
jboolean isCopyUsername;
const char * username = env->GetStringUTFChars(name, &isCopyUsername);
jboolean isCopypassword;
const char * password = env->GetStringUTFChars(pass, &isCopypassword);
result = authenticate(username, password);
env->ReleaseStringUTFChars(name, username);
env->ReleaseStringUTFChars(pass, password);
return result;
}
And finnally create a dll that i need to call from java. The dll is created and I load it well in java but I get this error log in java. What could I be Missing.
#
# A fatal error has been detected by the Java Runtime Environment:
#
# Internal Error (0xe0434352), pid=9708, tid=7756
#
# JRE version: 7.0-b147
# Java VM: Java HotSpot(TM) Client VM (21.0-b17 mixed mode, sharing windows-x86 )
# Problematic frame:
# C [KERNELBASE.dll+0x812f]
#
# Failed to write core dump. Minidumps are not enabled by default on client versions of Windows
First of all lets create a C# file like this:
using System;
public class Test{
public Test(){}
public String ping(){
return "C# is here.";
}
}
Then compile this with command below:
csc.exe /target:module Test.cs
You can find csc.exe in install path of .NET framework. After that create java file:
public class Test{
public native String ping();
public static void main(String[] args){
System.load("/path/to/dll");
System.out.println("Java is running.");
Test t = new Test();
System.out.println("Trying to catch C# " + r.ping());
}
}
javac Test.java This generates a Test.class.
javah -jni Test This generates a Test.h file which will be included in
C++ code.
After that we need to create our C++ file:
#include "stdafx.h"
#include "JAVA/Test.h"
#include "MCPP/Test.h"
#pragma once
#using <mscorlib.dll>
#using "Test.netmodule"
JNIEXPORT jstring JNICALL Java_Test_ping(JNIEnv *env, jobject obj){
Test^ t = gcnew Test();
String^ ping = t->ping();
char* str = static_cast<char*>((System::Runtime::InteropServices::Marshal::StringToHGlobalAnsi(ping)).ToPointer());
char cap[128];
strcpy_s(cap, str);
return env->NewStringUTF(cap);
}
Finally:
c:\>java Test
I hope this helps you. A basic example to use function C# in Java.
Sources:
https://www.quora.com/How-common-is-the-problem-of-calling-C-methods-from-Java-Do-many-developers-come-across-such-necessity
So I'm having some java-Code that I want to call from C++ via JNI. It's done by the following code:
int main(int argc, char **argv) {
JavaVM * jvm;
JNIEnv* env = create_vm(&jvm);
invoke_class( env );
}
JNIEnv* create_vm(JavaVM ** jvm) {
JNIEnv* env;
JavaVMInitArgs vm_args;
JavaVMOption options;
vm_args.version = JNI_VERSION_1_6;
vm_args.nOptions = 1;
options.optionString = "-Djava.class.path=<path_to_java_class>:<path_to_jars>";
vm_args.options = &options;
vm_args.ignoreUnrecognized = JNI_FALSE;
int ret = JNI_CreateJavaVM(jvm, (void **)&env, &vm_args);
if(ret < 0)
printf("\nUnable to Launch JVM\n");
return env;
}
void invoke_class(JNIEnv* env) {
jclass helloWorldClass;
helloWorldClass = env->FindClass("A");
if (env->ExceptionCheck()){
std::cout << "EXCEPTION OCCURED: " << std::endl;
env->ExceptionDescribe();
}
}
call_main(env, helloWorldClass);
void call_main(JNIEnv* env, jclass hClass){
jmethodID mainMethod;
jobjectArray applicationArgs;
jstring applicationArg0;
jstring applicationArg1;
mainMethod = env->GetStaticMethodID(hClass, "main", "([Ljava/lang/String;)V");
applicationArgs = env->NewObjectArray(2, env->FindClass("java/lang/String"), NULL);
applicationArg0 = env->NewStringUTF("planes.txt");
applicationArg1 = env->NewStringUTF("out.txt");
env->SetObjectArrayElement(applicationArgs, 0, applicationArg0);
env->SetObjectArrayElement(applicationArgs, 1, applicationArg1);
env->CallStaticVoidMethod(hClass, mainMethod, applicationArgs);
}
The corresponding Java-Code that is called is as follows:
public class A{
import org.mindswap.pellet.jena.PelletReasonerFactory;
import com.hp.hpl.jena.rdf.model.ModelFactory;
private OntModel model = ModelFactory.createOntologyModel(PelletReasonerFactory.THE_SPEC);
public A() {
System.out.println("hi2");
}
public static void main(String[] args) throws Exception {
System.out.println("hi1");
A app = new A();
long start = System.currentTimeMillis();
long stop = System.currentTimeMillis();
System.out.println("Processing took " + (stop - start) + " ms");
System.out.println("Normal program end.");
}
}
Okay, so if I'm now executing the C++-Code I get the following output:
hi1
without any error-messages, the program terminates correctly. However, I would expect the output to be something like:
hi1
hi2
Processing took 0ms
Normal program end.
It seems like calling the constructor just doesn't happen and the code after that isn't executed at all.
However, if I remove the line
private OntModel model = ModelFactory.createOntologyModel(PelletReasonerFactory.THE_SPEC);
the output is correct.
Since leaving out this isn't an option, what is happening here? How is it even possible that the program is terminating without an error-message although it shouldn't be finished at all?
I've tried to put in something like Thread.sleep(10000) to see if it has something to do with the time that given call takes, but that doesn't seem to be a problem at all.
EDIT: sry, I forgot to change the class-name.
EDIT2: I should mention that the java-code ist running perfectly fine when it is not called out of a C++-environment
Problem solved. It had something to do with the jar-files and some file-locations. Sorry for bothering for nothing!
I am writing a small C program using Cygwin that launches a Java Virtual Machine (libraries I am using require POSIX environment). So far, I have been able to get it to work as long as I place all of my classes in the same folder as the executable. However, I want to specify an actual JAR file that contains the application I want to run. This does not seem to work though, FindClass simply returns a null. I've narrowed it down to a problem with the classpath setting, like I said, because I can extract my jar file in the same directory as the executable and it will work. Here is a subset of my code:
I've loosely been following this guide: http://www.inonit.com/cygwin/jni/invocationApi/
int main( int argc, char *argv[] )
{
void* jvmDllHandle;
JNIEnv* jenv;
JavaVM* jvm;
JavaVMInitArgs args;
JavaVMOption options[1];
jclass cls;
jmethodID mainMethod;
jobjectArray appArgs;
jstring arg0;
assert( cygwin_internal( CW_SYNC_WINENV ) != 1UL );
jvmDllHandle = LoadLibrary( "c:\\Path\\To\\Application\\jre\\bin\\server\\jvm.dll" );
createJavaVM = dlsym( jvmDllHandle, "JNI_CreateJavaVM" );
args.version = JNI_VERSION_1_6;
args.nOptions = 1;
options[0].optionString = "-Djava.class.path=c:\\Path\\To\\Application\\TheJarFile.jar";
args.options = options;
args.ignoreUnrecognized = JNI_FALSE;
createJavaVM( &jvm, (void **) &jenv, &args );
cls = (*jenv)->FindClass( jenv, "some/package/MainClass" );
assert( cls != NULL ); // This fails.
/// Omitted...
return 0;
}
Tried using -classpath and -cp
int main( int argc, char *argv[] )
{
void* jvmDllHandle;
JNIEnv* jenv;
JavaVM* jvm;
JavaVMInitArgs args;
JavaVMOption options[1];
jclass cls;
jmethodID mainMethod;
jobjectArray appArgs;
jstring arg0;
assert( cygwin_internal( CW_SYNC_WINENV ) != 1UL );
jvmDllHandle = LoadLibrary( "c:\\Path\\To\\Application\\jre\\bin\\server\\jvm.dll" );
createJavaVM = dlsym( jvmDllHandle, "JNI_CreateJavaVM" );
args.version = JNI_VERSION_1_6;
args.nOptions = 1;
options[0].optionString = "-classpath c:\\Path\\To\\Application\\TheJarFile.jar";
args.options = options;
args.ignoreUnrecognized = JNI_FALSE;
createJavaVM( &jvm, (void **) &jenv, &args );
cls = (*jenv)->FindClass( jenv, "some/package/MainClass" );
assert( cls != NULL ); // This fails.
/// Omitted...
return 0;
}
How am I specifying the classpath incorrectly?
On x86-64, the Oracle Windows JDK headers define jint as long. This is 32 bits with Microsoft compilers (which the Oracle JDK is written for) but 64 bits with Cygwin gcc. Since JavaVMInitArgs contains some fields of this type, its binary layout is changed by this discrepancy.
I worked around this by providing a local jni.h header:
#include "stdint.h"
#define __int64 int64_t
#define long int32_t
#include "jni_md.h"
#undef long
#include_next "jni.h"
I'm including only jni_md.h within the scope of the long redefinition because it doesn't include any other headers, whereas jni.h includes a couple of standard headers which we would not want to be affected as well.
To ensure this is always included ahead of the Oracle header, use the -I compiler option to add its directory to the #include path.
I'm writing a simple program to call a Java function from my C program.
Following is my code:
#include <jni.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <pthread.h>
#include <time.h>
#include <stdlib.h>
JNIEnv* create_vm() {
JavaVM* jvm;
JNIEnv *env;
JavaVMInitArgs vm_args;
JavaVMOption options[1];
options[0].optionString - "-Djava.class.path=/home/chanders/workspace/Samples/src/ThreadPriorityTest";
vm_args.version = JNI_VERSION_1_6;
vm_args.nOptions = 1;
vm_args.options = &options;
vm_args.ignoreUnrecognized = JNI_FALSE;
JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
return env;
}
void invoke_class(JNIEnv* env) {
jclass helloWorldClass;
jmethodID mainMethod;
jobjectArray applicationArgs;
jstring applicationArg0;
helloWorldClass = (*env)->FindClass(env, "InvocationHelloWorld");
mainMethod = (*env)->GetStaticMethodID(env, helloWorldClass, "main", "([Ljava/lang/String;)V");
applicationArgs = (*env)->NewObjectArray(env, 1, (*env)->FindClass(env, "java/lang/String"), NULL);
applicationArg0 = (*env)->NewStringUTF(env, "From-C-program");
(*env)->SetObjectArrayElement(env, applicationArgs, 0, applicationArg0);
(*env)->CallStaticVoidMethod(env, helloWorldClass, mainMethod, applicationArgs);
}
int main() {
JNIEnv* env = create_vm();
invoke_class(env);
}
I'm compiling the above program using:
gcc -o invoke -I$JAVA_HOME/include/ -I$JAVA_HOME/include/linux -L$JAVA_HOME/jre/lib/amd64/server/ ThreadPriorityTest.c
and i'm getting the following error:
/tmp/ccllsK5O.o: In function `create_vm': ThreadPriorityTest.c:(.text+0x35): undefined reference to `JNI_CreateJavaVM' collect2: ld returned 1 exit status
I'm not really sure what is causing this problem
UPDATE 1
Included the -ljvm in the command line and then got a undefined reference to FUNCTION_NAME
I'm running it on Rhel 6.2
You've got the path to the Java library (the -L option), but not the library itself. You need to include -ljvm on the link line as well.