I've spent whole day on this problem and still have no idea how to solve it.
Here is the simplified code
JAVA
class javaclass{
private volatile boolean isTerminated;
public void javamethod()
{
log.logInfo("java :"+isTerminated());
}
public int isTerminated()
{
return (isTerminated) ? 1 : 0;
}
public doJob()
{
executeNative();
}
private native int executeNative() throws Exception;
}
C++
bool cmethod()
{
cerr << "JNI " << wrapper::isTerminated() << "\n";
if(wrapper::isTerminated)
return false;
jni->CallVoidMethod(self, apiJavamethod, xPoint);
return true;
//apiJavamethod is jMethodId of javamethod
}
int wrapper::isTerminated()
{
return jni->CallIntMethod(self, apiIsTerminated);
}
JNIEXPORT jint JNICALL NAMESPACE_javaclass_executeNative(JNIEnv* env, jobject self) {
for(int i=0;i < _ITERATIONS;++i)
{
if(!cmethod())
break;
}
}
C++ cmethod is executed in some loop, which stops when C++ catches isTerminated().
In output i see:
....
....
in log:
java 0
java 1
java 1
java 1
in console:
JNI 0
JNI 0
JNI 0
JNI 1
So, JNI is somehow behind the real state of variable.
It produces an important bug in application (((
Maybe it's some problem with java itself? (v.1.5, i forced to used it)
Any help is greatly appreciated.
I would suggest doing all the synchronization in one language or the other. It's looking like "volatile" isn't being respected across the boundary for some reason. Something like:
public doJob()
{
while(!isTerminated)
executeNative();
}
Related
I am trying to call Java methods from c++ using JNI. According to my understanding when c++ wants to access non static java methods we need to have instance to java to access those methods.
I am getting runtime error and I am not able to understand where exactly the error is but my code is breaking.I tracked by cout and its breaking while creating the instance
Error is : V [libjvm.so+0x2c3253] methodHandle::methodHandle(Method)+0x33*
And my code is like this:
.h file
static jclass clsEventStatus;
static jmethodID constructorEventStatus;
jobject EventStatusInstance;
.cpp file
jclass EventStatusJNI::clsEventStatus;
jmethodID EventStatusJNI::constructorEventStatus;
int EventStatusJNI::initialize() {
if(clsEventStatus==0) {
clsEventStatus = StubJava::env->FindClass("metronome/EventStatus");//no error
if(clsEventStatus == 0){
std::cout<<"class Eventstatus Not found";
}
constructorEventStatus = StubJava::env->GetMethodID(clsEventStatus, "<init>", "()V");//no error
return 0;
}
return 1;
}
EventStatusJNI::EventStatusJNI(){
EventStatusInstance = StubJava::env->NewObject(clsEventStatus, constructorEventStatus); //its breaking here when call happens
if(EventStatusInstance == 0 ){std::cout<<"No EventStatusInstance found ";}
}
In .java file:
public class EventStatus {
public EventStatus(){
System.out.println("Entered Event status.java");
setStatusValue(EventStatusValue.PROCESSING);
}
}
thank you for your time!
Calling get or set methods crashes. Object array is valid. It prints till line 2.Intially was getting error while inserting into the jobjectArray .Also tried with getting the value from Jobject .Both getter and setters fail .
JNICode is as below :
JNIEXPORT jint JNICALL Java_demo_JNIWrapper_pax_1store_1get_1data_1avail_1info
(JNIEnv *env, jclass jclass1, jobjectArray jobj)
{
.....
.....
int len = (*env)->GetArrayLength(env, jobj);
printf ("Incoming object array length = %d\n", len);// - - - > Works.. shows 2 (I am passing 2 objects)
jobject j = (*env)->GetObjectArrayElement(env, jobj,0);
printf("This line 1 \n ");
jmethodID meth1=(*env)->GetMethodID(env,jclass1,"getTimestamp","()Ljava/lang/String;");
printf("This line 2 \n "); // - - - - > Works..
jstring string_from_obj = (*env)->CallObjectMethod(env,j,meth1); // - - - - > Crashes..
printf("This line 3 \n "); // - - -> does not print this..
printf("Contents are =%s\n",(*env)->GetStringUTFChars(env, string_from_obj, 0));
............
}
My Java Object is as Below :
public class DataAvailable {
String timestamp;
public String getTimestamp() {
return timestamp;
}
public void setTimestamp(String timestamp) {
this.timestamp = timestamp;
}
}
Call to JNI is as below :
public native int pax_store_get_data_avail_info(DataAvailable[] stats_array);
The glaring issue is that pax_store_get_data_avail_info() is not a method of DataAvailable. It is a method of some other class? You don't actually say what it is. But let's suppose it is:
class X {
public native int pax_store_get_data_avail_info(DataAvailable[] stats_array);
}
When you call this method, the jclass jclass1 argument you get is for "X", not for DataAvailable. So your call to GetMethodID() probably fails. You don't check the return, so you re probably handing a bad method ID to the next call. JNI fails are hard crashes and very ugly.
You need to query the jclass for DataAvailable and use that.
I am writing a program that uses JNI to interface with a simple c program. I have created the following programs:
public static void main(String[] args) {
Hello h = new Hello();
System.out.println("before");
int number = h.sayHello();
System.out.println(number);
System.out.println("after");
}
and
JNIEXPORT int JNICALL Java_Hello_sayHello (JNIEnv *env, jobject obj) {
printf("Hello JNI\n");
return 10;
}
To my surprise this program returns:
before
10
after
Hello JNI
To me this is very strange because it is clear that the c program is executed between the "before" and "after" statement (The number 10 is printed). But why is the printf statement not executed when it is called. Is it somehow blocked by the jvm because only one program is allowed to write to the output at the same time? Is there a way to correct this behavior?
Yes. You will need to call flush.
In C, that is the fflush call -
printf("Hello JNI\n");
fflush(stdout);
return 10;
In Java, that is just flush on the Stream -
System.out.println("before");
System.out.flush();
I have the following JNI function that sends an event to my Java code.
void onIncrementAchievement(unsigned char _iArgumentCount, const void *_pArguments, void *_pUserData){
JNIEnv *pJNIEnv = GetJNIEnv();
if (pJNIEnv){
if ( _pArguments && ( _iArgumentCount > 0 ) ){
const S3DX::AIVariable *pVariables = (const S3DX::AIVariable *)_pArguments ;
if(_iArgumentCount != 2)
LOGI("INCORRECT NUMBER OF PARAMETERS");
else{
if(pVariables[0].GetType() == S3DX::AIVariable::eTypeString){
// CHANGE ME!
jclass pJNIActivityClass = pJNIEnv->FindClass ( "com/nurfacegames/testgame01/TestGame01" );
if(pJNIActivityClass == NULL)
LOGI("jclass was null!?!");
else{
jmethodID pJNIMethodID = pJNIEnv->GetStaticMethodID(pJNIActivityClass, "onIncrementAchievement", "(Ljava/lang/String;Ljava/lang/Integer;)V");
if(pJNIMethodID == NULL)
LOGI("jmethodID was null!?!?");
else{
//Create a new string
jstring arg;
arg = pJNIEnv->NewStringUTF(pVariables[0].GetStringValue());
jint arg2 = pVariables[1].GetNumberValue();
//Call the method and pass the string parameter along
pJNIEnv->CallStaticVoidMethod(pJNIActivityClass, pJNIMethodID, arg, arg2);
//Free the string
pJNIEnv->DeleteLocalRef(arg);
}
}
}
}
}
}
}
When I run my Android App, it force closes without even giving a single error in logcat, and when I compile with Ant, there are no compile errors.
I hate to post such a vague question, but if someone has an idea what is wrong with my JNI code, please give me a hint. Thanks!
The areas that I think have a problem (that I have been working on) are:
if(_iArgumentCount != 2)
LOGI("INCORRECT NUMBER OF PARAMETERS");
else{
and this section:
jint arg2 = pVariables[1].GetNumberValue();
pJNIEnv->CallStaticVoidMethod(pJNIActivityClass, pJNIMethodID, arg, arg2);
Thanks!
Well, look at your method name void onIncrementAchievement, as far as I know, that isn't the right way to declare your method in native section, it should be:
Java_com_example_yourpackagename_youractivityname_yourmethod(...)
And one more thing, the arguments in your method are also wrong I guess, check out this tutorial.
I´m trying to call java methods from simulink Matlab using JNI. I developed a small code in C++ that calls a main method which only prints helloworld on screen as a first test but at the line that it calls to find the class, matlab crashes.
The C++ code is this :
#include <stdio.h>
#include <jni.h>
#include "mex.h"
class MatlabAmbassador {
public:
MatlabAmbassador ();
//Destructor
~MatlabAmbassador ();
void run();
JNIEnv* create_vm() ;
void invoke_class(JNIEnv* env);
private:
}; // end class
MatlabAmbassador::MatlabAmbassador() {
}
MatlabAmbassador::~MatlabAmbassador() {
}
// -------------------------------------------------------------------------
JNIEnv* MatlabAmbassador::create_vm() {
JavaVM* jvm;
JNIEnv* env;
JavaVMInitArgs args;
JavaVMOption options[1];
long status;
/* There is a new JNI_VERSION_1_4, but it doesn't add anything for the purposes of our example. */
args.version = JNI_VERSION_1_6;
args.nOptions = 1;
options[0].optionString = "-Djava.class.path=C:\\Apps\\Projetos em java\\Portico\\Portico_Agent\\bin";
args.options = options;
args.ignoreUnrecognized = JNI_FALSE;
status=JNI_CreateJavaVM(&jvm, (void **)&env, &args);
return env;
}
// -------------------------------------------------------------------------
void MatlabAmbassador::invoke_class(JNIEnv* env) {
jclass helloWorldClass;
jmethodID mainMethod;
jobjectArray applicationArgs;
jstring applicationArg0;
mexPrintf("First\n");
helloWorldClass = env->FindClass("Teste"); <--- MATLAB CRASHES HERE
mexPrintf("second\n");
if (env->ExceptionOccurred()) {
env->ExceptionDescribe();
env->ExceptionClear() ;
}
if ( helloWorldClass == NULL ) {
mexPrintf( "%s%s\n", "Unable to obtain class reference for ",
helloWorldClass );
return;
} else {
mexPrintf( "%s%s\n", "Sucessfully created class reference for ",
helloWorldClass );
}
mainMethod = env->GetStaticMethodID( helloWorldClass, "main", "([Ljava/lang/String;)V");
applicationArgs = env->NewObjectArray(1, env->FindClass("java/lang/String"), NULL);
applicationArg0 = env->NewStringUTF( "From-C-program");
env->SetObjectArrayElement( applicationArgs, 0, applicationArg0);
env->CallStaticVoidMethod( helloWorldClass, mainMethod, applicationArgs);
}
// -------------------------------------------------------------------------
void MatlabAmbassador::run() {
char str [80];
mexPrintf(" INITIALIZING....\n" );
JNIEnv* env = create_vm();
invoke_class( env );
}
// -------------------------------------------------------------------------
Simulink Matlab has some methods to be used. One of this method is used to call the method run() described above. Here follows a piece of code:
static void mdlStart(SimStruct *S)
{
char *buf;
size_t buflen;
int status;
buflen = mxGetN((ssGetSFcnParam(S, 2)))*sizeof(mxChar)+1 ; // le o 3o parametro passado pela funcao (nome do arq)
buf = (char *)mxMalloc(buflen); // aloca memoria
status = mxGetString((ssGetSFcnParam(S, 2)), buf,(mwSize)buflen);
ssGetPWork(S)[0] = (void *) new MatlabAmbassador; // store new C++ object in the
MatlabAmbassador *c = (MatlabAmbassador *) ssGetPWork(S)[0];
c->run();
and the simple java code is:
public class Teste
{
public static void main(String[] args)
{
System.out.println(" INITALIZING...");
}
}
So, can anyone explain what am I missing or explain if there is a real problem calling JNI inside Matlab. Matlab is version 2011b and java version installed is JDK 1.0.6_45.
I´d appreciate any help as soon as possible.
Best regards
André Nudel
andre.nudel#gmail.com
Are you sure JNI_CreateJavaVM succeeds? Your code is not checking the returned status code or resulting env value (which is not getting initialized, so it may contain garbage), and the crash is apparently occurring at the first attempt to use env.
If you're running this inside Matlab, the JVM creation may be failing because there's already a JVM running inside the process as part of Matlab's normal environment. The JNI documents say that "creation of multiple VMs in a single process is not supported" (http://docs.oracle.com/javase/6/docs/technotes/guides/jni/spec/invocation.html#wp636). If this is what's happening, JNI_CreateJavaVM will return JNI_EEXIST (-5). Even if that's not what's going on here, it's good practice to check the status codes returned from functions that can fail. The examples in the JNI documentation omit error checking "for clarity", but you should include it in code you're actually going to run.
Check the status returned by JNI_CreateJavaVM and print it out to make sure it's succeeding. And maybe initialize env to 0 so it's clear whether you got a pointer from JNI or just random data.
I am not very familiar with Simulink, so I'm showing an example of a regular MEX-function instead.
As suggested by #AndrewJanke, I retrieve the existing JVM instance running inside the MATLAB process instead of creating a new one.
jni.cpp
#include "mex.h"
#include "jni.h"
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
// get MATLAB Java virtual machine instance
JavaVM *vm = NULL;
int n; jint res;
res = JNI_GetCreatedJavaVMs(&vm, 1, (jsize*) &n);
if (res != JNI_OK || n != 1) {
mexErrMsgIdAndTxt("mex:jni", "Couldn't find existing Java VM");
}
// get JNI interface instance
JNIEnv *env = NULL;
res = vm->GetEnv((void**) &env, JNI_VERSION_1_6);
if (res != JNI_OK || env == NULL) {
mexErrMsgIdAndTxt("mex:jni", "Couldn't get Java JNI environment");
}
mexPrintf("using Java %d.%d\n",
env->GetVersion() >> 16, env->GetVersion() & 0xFFFF);
}
I compiled the file using:
mex -I"C:\Program Files\Java\jdk1.6.0_45\include"
-I"C:\Program Files\Java\jdk1.6.0_45\include\win32" jni.cpp
"C:\Program Files\Java\jdk1.6.0_45\lib\jvm.lib"
Run in MATLAB, I get:
>> jni
using Java 1.6
You should be able to extend the example above to call an external Java class. Note that I have not been able to pass data between MATLAB MEX-API and Java JNI API (by that I mean passing Java objects as they are to a MEX-function, or the other way around, passing some kind of jobject back directly as is to MATLAB, without conversion that is), as in:
>> x = java.lang.Double(1)
>> my_mex_jni_fcn(x)
The problem is that mxArray is an opaque type, so I do not know how to interpret the pointer returned by mxGetData