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
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!
I would like to know whether JNI accepts the use of global variables declared in the .cpp (and of type created by me).
It would be for a speech recognition program. In fact, I would like to use C to declare all the settings of the decoder and Java to manage the recording thing on Android. I don't want to create a Decoder class on Java because it should be use both on Android and iOS (I would like to have 3 separate parts :
1) set up the decoder (only in C)
2) do the recording and manage the threads (only in Android-java)
3) decode the audio (in java but calling C functions that will use the settings of the decoder)
Example :
function.cpp :
struct decoder {
char *model;
};
In the function_2.ccp file :
decoder *ps;
void set_decoder(char *modelToUse){
ps->model = modelToUse;
return;
}
void process(int *buf ) {
process_raw(ps, buf); // modify buf accordingly to the parameters of ps
return;
}
char *getHyp (){
return ps->hyp;
}
void main(){
ps = set_decoder(var1);
jclass = android_class = (*env)->FindClass(env,"android/app/nameofactivity");
// find method_ID
jmethodID android_mid = (*env)->GetStaticMethodID(env, android_class, "java_method",String_signature);
// call method
jString hyp_java = (*env)->CallIntMethod(env, android_class, android_mid, int samplerate);
EDIT (add these lines) :
const char *hyp_tmp = (*env)->GetStringUTFChars(env, hyp_java, 0);
const char *hyp = hyp_tmp;
printf(hyp_java);
(*env)->ReleaseStringUTFChars(env, hyp_java, hyp_tmp);
//END of EDIT
}
In the java file (the methods process and getHyp are declared as native methods ; the read method is a method from Android):
file.java
String java_method(int samplerate){
int buf[samplerate];
recorder.read(buf);
this.process(buf);
return this.getHyp();
}
Is it possible to do that?
EDIT : In brief, I wonder whether I can declare a global variable "ps" in C and then call a java function that will call other C functions that will use the value of the declared variable "ps".
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 have a C++ program to receive binary data from network. After data is received, it will callback the data as a byte array from C++ to a Java client. I use the director feature in SWIG to easily achieve cross language polymorphism across C++ and Java for callback. Here's a link.
But I found there is no typemaps in SWIG from char* in C++ to byte[] in Java. So, I added a patch to the virous.i. Here's a link.
/*Director specific typemaps*/
%typemap(directorin, descriptor="[B") char *BYTE {
jbyteArray jb = (jenv)->NewByteArray(strlen(BYTE));
(jenv)->SetByteArrayRegion(jb, 0, strlen(BYTE), (jbyte*)BYTE);
$input = jb;
}
%typemap(directorout) char *BYTE {
$1 = 0;
if($input){
$result = (char *) jenv->GetByteArrayElements($input, 0);
if(!$1)
return $null;
jenv->ReleaseByteArrayElements($input, $result, 0);
}
}
%typemap(javadirectorin) char *BYTE "$jniinput"
%typemap(javadirectorout) char *BYTE "$javacall"
For example. I have a class in C++ called A:
class A{
public:
virtual void onDataReceived(const char* BYTE, const size_t len) {}
};
Then in Java code I have another class B to extend A:
class B extends A {
#Override
public void onDataReceived(byte[] data, long len) {
//handling the data.
}
}
I can receive the byte array in Java code, but it seems it is never garbage collected by JVM. The onDataReceived method in SWIG generated wrapper file is like this:
void SwigDirector_A::onDataReceived(char const *BYTE, size_t const len) {
JNIEnvWrapper swigjnienv(this);
JNIEnv * jenv = swigjnienv.getJNIEnv();
jobject swigjobj = (jobject) NULL;
jbyteArray jBYTE = 0;
jlong jlen;
if (!swig_override[3]) {
A::onDataReceived(BYTE,len);
return;
}
swigjobj = swig_get_self(jenv);
if (swigjobj && jenv->IsSameObject(swigjobj, NULL) == JNI_FALSE) {
{
jbyteArray jb = (jenv)->NewByteArray(strlen(BYTE));
(jenv)->SetByteArrayRegion(jb, 0, strlen(BYTE), (jbyte*)BYTE);
jBYTE = jb;
}
jlen = (jlong) len;
jenv->CallStaticVoidMethod(Swig::jclass_DataTransferJNI, Swig::director_methids[3], swigjobj, jBYTE, jlen);
if (jenv->ExceptionCheck() == JNI_TRUE) return;
} else {
SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "null upcall object");
}
if (swigjobj) jenv->DeleteLocalRef(swigjobj);
}
In my C++ code, I will delete the data in the receive buffer after the callback is done. But I checked from Java VisualVM that the memory used by java client process is not GC-ed after a long time. Thanks advance for any help!
PS. The data is quite large, it is around 32KB.
I got it solved by added deletion of reference to jb in Director typemaps: (jenv)->DeleteLocalRef(jb);
Here's the updated patch.
I'm trying to access the message in a jthrowable while handing an exception generated when I fail to find a class. However, I am unable to access the message ID of getMessage() on the jthrowable object, and I don't know why. I've tried changing the signature of getMessage to "()Ljava/lang/String" (without the semicolon at the end, but that's necessary, right?) with no joy. I'm confused as hell about this. I even tried replacing getMessage with toString, and that didn't work. Obviously I'm doing something trivially wrong here.
Here's the code I'm using:
jthrowable java_exception;
jclass java_class;
jmethodID method;
java_exception = (*jEnv)->ExceptionOccurred(jEnv);
assert (java_exception != NULL);
java_class = (*jEnv)->GetObjectClass (jEnv, java_exception);
assert (java_class != NULL);
method = (*jEnv)->GetMethodID (jEnv, java_class, "getMessage", "()Ljava/lang/String;");
if (method == NULL) {
printf ("Seriously, how do I get here?!\n");
(*jEnv)->ExceptionDescribe (jEnv);
return;
}
The output of this code (amongst other things) looks like this:
Seriously, how do I get here?!
Exception in thread "main" java.lang.NoClassDefFoundError: com/planet/core360/docgen/Processor
javap -p -s java.lang.Throwable gives me this:
Compiled from "Throwable.java"
public class java.lang.Throwable extends java.lang.Object implements java.io.Serializable{
...
public java.lang.String getMessage();
Signature: ()Ljava/lang/String;
...
Okay, so it looks like my problem was that GetObjectClass doesn't act the way you'd expect it to on a jthrowable, or at least the results of it are not useful for the purposes of getting methods. Replacing that portion of the code with this works:
java_class = (*jEnv)->FindClass (jEnv, "java/lang/Throwable");
method = (*jEnv)->GetMethodID (jEnv, java_class, "getMessage", "()Ljava/lang/String;");
Damnably odd, that. I hope this helps someone else in the future, though.
I tried your approach, and it worked for me. A few things though: I'm using the C++ interface (though that shouldn't make a difference), and I'm using Java 6 update 10, x64 edition, on Ubuntu 8.04. Perhaps the Java version and/or platform used will make a difference.
#include <cstdio>
#include <jni.h>
int
main(int argc, char** argv)
{
if (argc != 3) {
std::fprintf(stderr, "usage: %s class message\n", argv[0]);
return 1;
}
JavaVM* jvm;
void* penv;
JavaVMInitArgs args = {JNI_VERSION_1_6};
if (jint res = JNI_CreateJavaVM(&jvm, &penv, &args)) {
std::fprintf(stderr, "Can's create JVM: %d\n", res);
return -res;
}
JNIEnv* env(static_cast<JNIEnv*>(penv));
jint vers(env->GetVersion());
std::printf("JNI version %d.%d\n", vers >> 16, vers & 0xffff);
env->ThrowNew(env->FindClass(argv[1]), argv[2]);
jthrowable exc(env->ExceptionOccurred());
std::printf("Exception: %p\n", exc);
if (exc) {
jclass exccls(env->GetObjectClass(exc));
jclass clscls(env->FindClass("java/lang/Class"));
jmethodID getName(env->GetMethodID(clscls, "getName", "()Ljava/lang/String;"));
jstring name(static_cast<jstring>(env->CallObjectMethod(exccls, getName)));
char const* utfName(env->GetStringUTFChars(name, 0));
jmethodID getMessage(env->GetMethodID(exccls, "getMessage", "()Ljava/lang/String;"));
jstring message(static_cast<jstring>(env->CallObjectMethod(exc, getMessage)));
char const* utfMessage(env->GetStringUTFChars(message, 0));
std::printf("Exception: %s: %s\n", utfName, utfMessage);
env->ReleaseStringUTFChars(message, utfMessage);
env->ReleaseStringUTFChars(name, utfName);
}
return -jvm->DestroyJavaVM();
}
I've used jnitest java/lang/InternalError 'Hello, world!' for my testing; feel free to try with different exception types!