How to call org.jdom.Element APIs using JNI C++ - java

New to JNI. I am trying to call Element::getChild and Element::getChildText APIs (java org.jdom.Element) to get the version number of a system that is stored in "settings.xml" This xml file is archived in JAR file. Assuming the root element is available, here is what I am doing:
jstring fileNameStr = env->NewStringUTF("settings.xml");
jobject rootElement = env->CallStaticObjectMethod(a_class, xmlRootElement_mid, fileNameStr);
jclass cls_element = env->FindClass("org/jdom/Element");
/*get method ID for getChild() & getChildText() */
jmethodID getChild_mid = env->GetMethodID(cls_element, "getChild", "(Ljava/lang/String;)Lorg/jdom/Element;");
jmethodID getChildText_mid = env->GetMethodID(cls_element, "getChildText", "(Ljava/lang/String;)Ljava/lang/String;");
jstring aboutStr = env->NewStringUTF("about");
jobject about = env->CallStaticObjectMethod(cls_element, getChild_mid, aboutStr); ---> Seg Faults!!!
Basically, I want to do Java equivalent of this:
In Java:
import org.jdom.Element;
...
Element element = SomeMethodToReadXmlFile("settings.xml");
version = element.getChild("about").getChildText("version"); <---- works
How should I do this?

Using JNI, here is what worked for me that uses org.jdom.Element. My settings.xml file looks like this:
<?xml version="1.0">
<settings>
<about>
<version>1.0</version>
</about>
</settings>
JNI C++:
jstring fileNameStr = env->NewStringUTF("settings.xml");
// assuming xmlRootElement_mid is known
jobject element_obj = env->CallStaticObjectMethod(a_class, xmlRootElement_mid, fileNameStr);
jclass cls_element = env->FindClass("org/jdom/Element");
/*get method ID for getChild() & getChildText() */
jmethodID getChild_mid = env->GetMethodID(cls_element, "getChild", "(Ljava/lang/String;)Lorg/jdom/Element;");
jmethodID getChildText_mid = env->GetMethodID(cls_element, "getChildText", "(Ljava/lang/String;)Ljava/lang/String;");
jstring jstr = env->NewStringUTF("about");
element_obj = env->CallObjectMethod(element_obj, getChild_mid, jstr)
jstr = env->NewStringUTF("version");
jstring version_str = (jstring)env->CallObjectMethod(element_obj, getChildText_mid, jstr);
//Convert to std::string
std::string version_std_str;
if (version_str)
{
const char *c = env->GetStringUTFChars(version_str, 0);
version_std_str = std::string(c);
env->ReleaseStringUTFChars(version_str, c);
}
std::cout << version_std_str << std::endl
Outputs:
1.0

Related

When I create a stack strcuct variable in jni part and send it by value to native, junk is sent

When I create an in stack variable from a struct in jni cpp and send it to a function in cpp, junk data is sent in the struct.
The strange thing is that if I print some data from the struct before sending it, the program runs correctly
JNIEXPORT void JNICALL Java_dev_eng_gdx_database_DbSquirrel_jniSetBodyLayer(JNIEnv* env, jobject object, jlong address, jshort id, jfloat zDisplacement, jboolean useVecFriction, jfloat friction, jfloat frictionX, jfloat frictionY, jboolean useSlopePhysics, jboolean useSlopeRotation, jboolean rollOnMove, jboolean useVecRoll, jfloat roll, jfloat rollX, jfloat rollY) {
BodyLayer bodyLayer = {.zDisplacement = zDisplacement, .useVecFriction = useVecFriction, .friction = friction, .vecFriction = {frictionX, frictionY},
.useSlopePhysics = useSlopePhysics, .useSlopeRotation = useSlopeRotation,
.rollOnMove = rollOnMove, .useVecRoll = useVecRoll, .roll = roll, .vecRoll = {rollX, rollY} };
//std::cout << "useVecFriction " << bodyLayer.useVecFriction << "\n";
//std::cout << "friction " << friction << "\n";
((SquirrelDb*)address)->setBodyLayer(toC2(id), bodyLayer);
}
If I run the code printing the struct data whith std::cout, the program runs correctly.
If I run the code without printing the struct data it throws this error:
AL lib: (EE) alc_cleanup: 1 device not closed
Thanks ind advance

Java-Program terminating early without error-message after being called from C++ via JNI

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!

Accessing accelerometer from jni

I'm trying to make an android app in which I need to access accelerometer in jni.
java code :
mSensorManager = (SensorManager)getSystemService(SENSOR_SERVICE);
mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
jni code :
jclass cls_context = env->FindClass("android/content/Context");
jfieldID tService = env->GetStaticFieldID(cls_context, "SENSOR_SERVICE","Ljava/lang/String;");
jstring jstr = (jstring)env->GetStaticObjectField(cls_context, tService);
//getSystemService(SENSOR_SERVICE);
jclass cls_act = env->GetObjectClass(activity);
jmethodID GetSystemService = env->GetMethodID(cls_act,"getSystemService","(Ljava/lang/String;)Ljava/lang/Object;");
jobject systemservice = env->CallObjectMethod(activity,GetSystemService,jstr);
//getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
jclass sensormanager = env->GetObjectClass(systemservice);
jmethodID getdefaultsensor = env->GetMethodID(sensormanager, "getDefaultSensor", "(I)Landroid/hardware/Sensor;");
jobject sensor = env->CallObjectMethod(systemservice, getdefaultsensor, "Sensor.TYPE__ACCELEROMETER");
But jobject sensor is getting null.
Sensor.TYPE_ACCELEROMETER is an inlined constant of type int and has value 1, which should be passed there.
But anyway those 2 lines split into 9 lines of JNI code looks bad. Wouldn't it be better to leave those original lines of Java code and then just call it from JNI?

JNI JVM Invocation Classpath

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.

Pass string array to NDK JNI method

I am trying following code inside cpp file to receive arraylist from java file. Here is the code
jobjectArray trackables;
JNIEXPORT void JNICALL
Java_com_putitout_buck_VideoPlayback_setTrackables(JNIEnv *env, jobject obj, jobject trackables_)
{
// use the Array list
jclass ArrayList_class = env->FindClass( "java/util/ArrayList" );
// to conver jobject to jstring
jmethodID caster = env->GetMethodID(ArrayList_class, "toString", "()Ljava/lang/String;");
// get two methods
jmethodID Get_method = env->GetMethodID( ArrayList_class, "get", "(I)Ljava/lang/Object" );
jmethodID Size_method = env->GetMethodID( ArrayList_class, "size", "()I" );
// call java.lang.ArrayList.get()
int NumElts = env->CallIntMethod(trackables_, Size_method);
// allocate output array
jclass objClass = env->FindClass("java/lang/String");
trackables = env->NewObjectArray(NumElts, objClass, 0);
// fetch all the items
for(int i = 0 ; i < NumElts ; i++)
{
// call java.lang.ArrayList.get(int index) method
// Not sure about the parameter passing here
jobject Tmp = env->CallObjectMethod(trackables_, Get_method, i);
jstring Str = (jstring)env->CallObjectMethod(Tmp, caster);
__android_log_print(ANDROID_LOG_ERROR, "TRACKERS", "%s", Str);
// get the length
int StrLen = env->GetStringLength(Str);
env->SetObjectArrayElement(trackables, i, Str);
}
}
Following is the code I am declaring in java file
public native void setTrackables(ArrayList<String> trackables);
Following is in the log.
10-24 17:45:44.555: W/dalvikvm(27400): JNI WARNING: JNI method called with exception pending
10-24 17:45:44.555: W/dalvikvm(27400): in Lcom/putitout/buck/VideoPlayback;.setTrackables:(Ljava/util/ArrayList;)V (GetMethodID)
10-24 17:45:44.555: W/dalvikvm(27400): Pending exception is:
10-24 17:45:44.555: E/dalvikvm(27400): VM aborting
10-24 17:45:44.560: A/libc(27400): Fatal signal 11 (SIGSEGV) at 0xdeadd00d (code=1)
Why are you using JNI to convert an ArratList of objects into an array of strings? Better do that in Java - much cleaner code - and pass the array of strings to JNI.
That said, LogCat says one of your Java method calls is causing an exception. It does not say which one. To debug, insert a check after every CallXXXMethod:
if(env->ExceptionOccurred())
{
__android_log_write(ANDROID_LOG_ERROR, "TRACKERS", "Error in (MethodName)");
env->ExceptionDescribe();
}
Also, "any help?" comment is not constructive. Please don't rush the community. This is a free service, no one here owes you an answer.

Categories