env->FindClass("java.lang.Math"); - java

env->FindClass("java.lang.Math"); fails. Why?
gcc -I/System/Library/Frameworks/JavaVM.framework/Headers test.cpp -framework JavaVM -o test && ./test
http://developer.apple.com/library/mac/#samplecode/simpleJavaLauncher/Listings/utils_h.html#//apple_ref/doc/uid/DTS10000688-utils_h-DontLinkElementID_7
http://developer.apple.com/library/mac/#technotes/tn2147/_index.html
#include <jni.h>
#include <stdlib.h>
int main() {
printf("START.\n");
JavaVM* jvm = NULL;
JNIEnv *env;
JavaVMInitArgs vm_args;
JNI_GetDefaultJavaVMInitArgs(&vm_args);
vm_args.version = JNI_VERSION_1_6;
vm_args.nOptions = 0;
int ret = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
if(ret < 0) {
printf("Unable to Launch JVM\n");
return 1;
}
jclass mathClass = env->FindClass("java.lang.Math");
if (mathClass == NULL) {
printf("Unable to find java.lang.Math\n");
return 1;
}
jmethodID cosMethod = env->GetStaticMethodID(mathClass, "cos", "(D)D");
if (cosMethod == NULL) {
printf("Unable to find java.lang.Math.cos()\n");
return 1;
}
printf("call\n");
jdouble jIn = 0.1;
jdouble jOut = env->CallStaticIntMethod(mathClass, cosMethod, jIn);
printf("jOut: %f", jOut);
printf("DestroyJavaVM.\n");
jvm->DestroyJavaVM();
printf("END.\n");
return 0;
}

You should be calling:
jclass mathClass = env->FindClass("java/lang/Math");
From the documentation:
name: fully-qualified class name (that is, a package name, delimited by “/”, followed by the class name). If the name begins with “[“ (the array signature character), it returns an array class.

Try:
env->FindClass("java/lang/Math")

Related

Embed java code from a C++ library without needing top-level program to embed it?

I'm using QtCreator to deploy C++/Java applications on Android. But I think my problem may not be specific to the way QtCreator deploys the app.
I want to create a C++ library providing a specific functionnality. To do so, the library needs to instantiate a Java class, this last one will be used to do some SDK functions class (for stuff that are not available in the NDK/C++ API).
Creating and using java objects from a C++ program works fine. I package the .java file to the application environment during compilation/deployment and then I can use the Java class via two approachs:
Declare JNI_OnLoad, load class id, method id, and later call them using jni
Use Qt QAndroidJniObject objects (this is specific to QtCreator)
Now the problem comes when I want to create and use java objects from a C++ library. It only works if the .java file is packaged with the top-level application. I could not find a way to package the java with and only with the library itself. Meaning that anyone why needs to use my library will not only have to simply link with the library, but will also need to package the .java file(s) needed by my library. This breaks encapsulation and gives a hard time to the end developer writing programs and simply wanting to load a library and expecting it to embed all it needs to work on its own...
My question is: How can the library embed the java file, so that this java file does not need to be part of the top level program package to let the library use it?
Here is a quick sample: MainWindow constrctor calls 4 functions themselves trying to create and use Java objects. Only the first two calls work...
main.cpp:
#include <QApplication>
#include <QMainWindow>
#include "MyLib.h"
#include <QtAndroidExtras/QAndroidJniObject>
#include "jni.h"
#include <assert.h>
// load java classes from main program
JavaVM* s_javaVM = NULL;
jclass s_classID = 0;
jmethodID s_ctorMethodID = 0;
jmethodID s_callmethodID = 0;
bool loadJava( JNIEnv *env )
{
jclass clazz = env->FindClass("my/FooPrg");
if (!clazz)
{
qCritical("Can't find FooPrg class");
return false;
}
// keep a global reference to it
s_classID = (jclass)env->NewGlobalRef(clazz);
// search for its contructor
s_ctorMethodID = env->GetMethodID(s_classID, "<init>", "()V");
if (!s_ctorMethodID )
{
qCritical("Can't find class contructor");
return false;
}
// search for a method
s_callmethodID = env->GetMethodID(s_classID, "Mult", "(I)I");
if (!s_callmethodID )
{
qCritical("Can't find Mult method");
return false;
}
return true;
}
jint JNICALL JNI_OnLoad(JavaVM *vm, void *)
{
s_javaVM = vm;
JNIEnv* env = NULL;
if (s_javaVM->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_4) != JNI_OK)
return -1;
loadJava( env );
return JNI_VERSION_1_4;
}
void callJavaFunctionFromPrgWithQt()
{
if ( QAndroidJniObject::isClassAvailable("my/FooPrg") )
{
QAndroidJniObject obj("my/FooPrg","()V");
if ( obj.isValid() )
{
jint res = obj.callMethod<jint>("Mult", "(I)I", 0x0002);
assert( res == 4 );
}
else
{
assert( false );
}
}
else
{
assert( false );
}
}
void callJavaFunctionFromPrgWithJniLoad()
{
if ( s_classID != 0 && s_ctorMethodID != 0 && s_callmethodID != 0 )
{
JNIEnv* env = NULL;
if (s_javaVM->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_4) != JNI_OK)
assert(false);
jobject j_object = env->NewGlobalRef( env->NewObject(s_classID, s_ctorMethodID ) );
jint res = env->CallIntMethod(j_object, s_callmethodID, 0x0002 );
assert( res == 4 );
}
else
{
assert( false );
}
}
class MainWindow : public QMainWindow
{
public:
MainWindow()
{
callJavaFunctionFromPrgWithQt(); // works
callJavaFunctionFromPrgWithJniLoad(); // works
callJavaFunctionFromLibWithQt(); // fails, assert
callJavaFunctionFromLibWithJniLoad(); // fails, because libraries JNI_OnLoad can't find FooLib.java!
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
MyLib.h:
#pragma once
void callJavaFunctionFromLibWithQt();
void callJavaFunctionFromLibWithJniLoad();
MyLib.cpp:
#include "MyLib.h"
#include <QtAndroidExtras/QAndroidJniObject>
#include "jni.h"
#include <assert.h>
// load java classes from main program
JavaVM* s_javaVM = NULL;
jclass s_classID = 0;
jmethodID s_ctorMethodID = 0;
jmethodID s_callmethodID = 0;
bool loadJava( JNIEnv *env )
{
jclass clazz = env->FindClass("my/FooLib");
if (!clazz)
{
qDebug("Can't find FooLib class");
return false;
}
// keep a global reference to it
s_classID = (jclass)env->NewGlobalRef(clazz);
// search for its contructor
s_ctorMethodID = env->GetMethodID(s_classID, "<init>", "()V");
if (!s_ctorMethodID )
{
qDebug("Can't find class contructor");
return false;
}
// search for a method
s_callmethodID = env->GetMethodID(s_classID, "Mult", "(I)I");
if (!s_callmethodID )
{
qDebug("Can't find Mult method");
return false;
}
return true;
}
jint JNICALL JNI_OnLoad(JavaVM *vm, void *)
{
s_javaVM = vm;
JNIEnv* env = NULL;
if (s_javaVM->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_4) != JNI_OK)
return -1;
// uncommenting this makes the application crash upon load....
//loadJava( env );
return JNI_VERSION_1_4;
}
void callJavaFunctionFromLibWithQt()
{
if ( QAndroidJniObject::isClassAvailable("my/FooLib") )
{
QAndroidJniObject obj("my/FooLib","()V");
if ( obj.isValid() )
{
jint res = obj.callMethod<jint>("Mult", "(I)I", 0x0002);
assert( res == 4 );
}
else
{
assert( false );
}
}
else
{
assert( false ); // this assertion is reached!
}
}
void callJavaFunctionFromLibWithJniLoad()
{
if ( s_classID != 0 && s_ctorMethodID != 0 && s_callmethodID != 0 )
{
JNIEnv* env = NULL;
if (s_javaVM->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_4) != JNI_OK)
assert(false);
jobject j_object = env->NewGlobalRef( env->NewObject(s_classID, s_ctorMethodID ) );
jint res = env->CallIntMethod(j_object, s_callmethodID, 0x0002 );
assert( res == 4 );
}
else
{
assert( false ); // this assertion is reached!
}
}
FooPrg.java:
package my;
import java.lang.Integer;
public class FooPrg
{
public FooPrg()
{
}
public int Mult(int val)
{
return val * 2;
}
}
FooLib.java:
package my;
import java.lang.Integer;
public class FooLib
{
public FooLib()
{
}
public int Mult(int val)
{
return val * 2;
}
}
jniload.pro:
TARGET = jniload
CONFIG += qt resources
QT += core gui widgets
android: QT += androidextras
SOURCES += src/main.cpp
TEMPLATE = app
INCLUDEPATH += ifc
LIBS += \
-l$$OUT_PWD/../../lib/jniload_lib/libjniload_lib.so
ANDROID_EXTRA_LIBS += \
$$OUT_PWD/../../lib/jniload_lib/libjniload_lib.so
ANDROID_PACKAGE_SOURCE_DIR = data/android/root
OTHER_FILES += data/android/root/src/my/FooPrg.java
jniload_lib.pro:
TARGET = jniload_lib
CONFIG += qt resources
QT += core gui widgets
android: QT += androidextras
SOURCES += src/MyLib.cpp
HEADERS += ifc/MyLib.h
TEMPLATE = lib
INCLUDEPATH += ifc
# This does has apparently no effect on library
ANDROID_PACKAGE_SOURCE_DIR = data/android/root
OTHER_FILES += data/android/root/src/my/FooLib.java
Finaly got a way to work this out.
I removed ANDROID_PACKAGE_SOURCE_DIR line from jniload.pro file and hanlde manual copy of the .java files through custom build steps:
custom_jniload_lib_step.target = jniload_lib_mockup.h
custom_jniload_lib_step.commands = $(COPY_DIR) data\android\root ..\..\android-build
QMAKE_EXTRA_TARGETS += custom_jniload_lib_step
PRE_TARGETDEPS += jniload_lib_mockup.h
custom_jniload_step.target = jniload_mockup.h
custom_jniload_step.commands = $(COPY_DIR) data\android\root ..\..\android-build
QMAKE_EXTRA_TARGETS += custom_jniload_step
PRE_TARGETDEPS += jniload_mockup.h
Then, upon deployment, android-build/src contains both FooLib.java and FooPrg.java and then both library and program can access them!

java.lang.UnsatisfiedLinkError: Couldn't load cpuinfo from loader dalvik.system.PathClassLoader

I am developing an app requiring CPU information. I placed the cpuinfo library in the jni folder. but i got this error:
java.lang.UnsatisfiedLinkError: Couldn't load cpuinfo from loader
dalvik.system.PathClassLoader[dexPath=/data/app/com.iman.marashi.deviceinfo-2.apk,libraryPath=/data/app-lib/com.iman.marashi.deviceinfo-2]:
findLibrary returned null
that error got from this code:
static {
try {
System.loadLibrary("cpuinfo");
isAvailable = true;
} catch (UnsatisfiedLinkError ule) {
Log.e("Loading cpuinfo native library", "" + ule);
}
cpuinfo.c code:
#include <jni.h>
#include <stdio.h>
#include <stdlib.h>
#include <cpu-features.h>
#include <android/log.h>
#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, "jason", __VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, "jason", __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, "jason", __VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, "jason", __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, "jason", __VA_ARGS__)
const int cpu_features_list[] = {
ANDROID_CPU_ARM_FEATURE_ARMv7,
ANDROID_CPU_ARM_FEATURE_VFPv3,
ANDROID_CPU_ARM_FEATURE_NEON,
ANDROID_CPU_ARM_FEATURE_LDREX_STREX,
ANDROID_CPU_ARM_FEATURE_VFPv2,
ANDROID_CPU_ARM_FEATURE_VFP_D32,
ANDROID_CPU_ARM_FEATURE_VFP_FP16,
ANDROID_CPU_ARM_FEATURE_VFP_FMA,
ANDROID_CPU_ARM_FEATURE_NEON_FMA,
ANDROID_CPU_ARM_FEATURE_IDIV_ARM,
ANDROID_CPU_ARM_FEATURE_IDIV_THUMB2,
ANDROID_CPU_ARM_FEATURE_iWMMXt,
ANDROID_CPU_X86_FEATURE_SSSE3,
ANDROID_CPU_X86_FEATURE_POPCNT,
ANDROID_CPU_X86_FEATURE_MOVBE
};
const char cpu_features_string_list[][20] = {
"armv7",
"vfpv3",
"neon",
"ldrex_strex",
"vfpv2",
"vfp_d32",
"vfp_fp16",
"vfp_fma",
"neon_fma",
"idiv_arm",
"idiv_thumb2",
"iwmmxt",
"ssse3",
"popcnt",
"movbe"
};
void appendString(char *strDestination, char *strSource) {
strcat(strDestination, " ");
strcat(strDestination, strSource);
}
char* get_cpu_family() {
if (android_getCpuFamily() == ANDROID_CPU_FAMILY_ARM) {
return "ARM";
} else if (android_getCpuFamily() == ANDROID_CPU_FAMILY_X86) {
return "X86";
} else if (android_getCpuFamily() == ANDROID_CPU_FAMILY_MIPS) {
return "MIPS";
} else {
return "Unknown";
}
}
char* get_cpu_features() {
char cpu_features[256] = "";
uint64_t features = android_getCpuFeatures();
int i = 0;
int features_length = sizeof(cpu_features_list) / sizeof(int);
for (i = 0; i < features_length; i++) {
if (features & cpu_features_list[i]) {
appendString(cpu_features, cpu_features_string_list[i]);
}
}
return cpu_features;
}
jstring
Java_hardwareinfo_HardwareInfoActivity_getCpuFamilyFromJNI( JNIEnv* env,
jobject thiz )
{
return (*env)->NewStringUTF(env, get_cpu_family());
}
jstring
Java_hardwareinfo_HardwareInfoActivity_getCpuCountFromJNI( JNIEnv* env,
jobject thiz )
{
return android_getCpuCount();
}
jstring
Java_hardwareinfo_HardwareInfoActivity_getCpuFeaturesFromJNI( JNIEnv* env,
jobject thiz )
{
char buffer[256];
strlcpy(buffer, get_cpu_features(), sizeof buffer);
return (*env)->NewStringUTF(env, buffer);
}
Application.mk code:
# Build both ARMv5TE and ARMv7-A machine code.
APP_ABI := armeabi armeabi-v7a
Android.mk code:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := cpuinfo
LOCAL_SRC_FILES := cpuinfo.c
ifeq ($(TARGET_ARCH_ABI),armeabi-v7a)
LOCAL_CFLAGS := -DHAVE_NEON=1
#LOCAL_SRC_FILES += cpuinfo-intrinsics.c.neon
endif
LOCAL_STATIC_LIBRARIES := cpufeatures
LOCAL_LDLIBS := -llog
include $(BUILD_SHARED_LIBRARY)
$(call import-module,cpufeatures)

Getting /usr/bin/ld: cannot find -ljvm collect2: ld returned 1 exit status make: *** [hello_world] Error 1 while trying to call java class method

I m trying to call java class methods from c with JNI.I have made a simple hello_world.java and hello_world.c and a Makefile
while executing make command i am getting following errors . please help me out
make
gcc hello_world.c -o hello_world \
-L /usr/java/latest/jre/lib/i386/server/ \
-ljvm \
-I /usr/java/latest/include/ \
-I /usr/java/latest/include/linux/ \
hello_world.c
/usr/bin/ld: skipping incompatible /usr/java/latest/jre/lib/i386/server//libjvm.so when searching for -ljvm
/usr/bin/ld: cannot find -ljvm
collect2: ld returned 1 exit status
make: *** [hello_world] Error 1
public class hello_world{
public static void main(String[] args){
System.out.println("Hello, World");
}
public static int square(int input){
int output = input * input;
return output;
}
public static int power(int input, int exponent){
int output,i;
output=1;
for(i=0;i<exponent;i++){
output *= input;
}
return output;
}
}
#include <stdio.h>
#include <jni.h>
JNIEnv* create_vm(JavaVM **jvm)
{
JNIEnv* env;
JavaVMInitArgs args;
JavaVMOption options;
args.version = JNI_VERSION_1_6;
args.nOptions = 1;
options.optionString = "-Djava.class.path=./";
args.options = &options;
args.ignoreUnrecognized = 0;
int rv;
rv = JNI_CreateJavaVM(jvm, (void**)&env, &args);
if (rv < 0 || !env)
printf("Unable to Launch JVM %d\n",rv);
else
printf("Launched JVM! :)\n");
return env;
}
void invoke_class(JNIEnv* env)
{
jclass hello_world_class;
jmethodID main_method;
jmethodID square_method;
jmethodID power_method;
jint number=20;
jint exponent=3;
hello_world_class = (*env)->FindClass(env, "helloWorld");
main_method = (*env)->GetStaticMethodID(env, hello_world_class, "main", "([Ljava/lang/String;)V");
square_method = (*env)->GetStaticMethodID(env, hello_world_class, "square", "(I)I");
power_method = (*env)->GetStaticMethodID(env, hello_world_class, "power", "(II)I");
(*env)->CallStaticVoidMethod(env, hello_world_class, main_method, NULL);
printf("%d squared is %d\n", number,
(*env)->CallStaticIntMethod(env, hello_world_class, square_method, number));
printf("%d raised to the %d power is %d\n", number, exponent,
(*env)->CallStaticIntMethod(env, hello_world_class, power_method, number, exponent));
}
int main(int argc, char **argv)
{
JavaVM *jvm;
JNIEnv *env;
env = create_vm(&jvm);
if(env == NULL)
return 1;
invoke_class(env);
return 0;
}
all:run
hello_world.class:hello_world.java
/usr/java/latest/bin/javac hello_world.java
hello_world: hello_world.c
gcc -o hello_world \
-L /usr/java/latest/jre/lib/i386/server/ \
-ljvm \
-I /usr/java/latest/include/ \
-I /usr/java/latest/include/linux/ \
hello_world.c
run: hello_world.class hello_world
export LD_LIBRARY_PATH="/usr/java/latest/jre/lib/i386/server/"
./hello_world
clean:
rm -f hello_world.class hello_world
-ljvm will be transform by GCC to libjvm.so and it try find it into the -L paths.
try to look into you folder if you find the good .so

How to properly reuse a JNI JVM in C++?

I have a C++ class that uses a JNI JVM to run a Java method. For whatever reason my program is working for 3 iterations and on the 3rd loop the JVM crashes with a SIGSEGV.
#
# A fatal error has been detected by the Java Runtime Environment:
#
# SIGSEGV (0xb) at pc=0x00007f2af5de1c81, pid=7669, tid=139822523557696
#
# JRE version: Java(TM) SE Runtime Environment (7.0_45-b18) (build 1.7.0_45-b18)
# Java VM: Java HotSpot(TM) 64-Bit Server VM (24.45-b08 mixed mode linux-amd64 compressed oops)
# Problematic frame:
# C [libc.so.6+0x7ac81]
This code is not complete at all.
#include <string>
#include <thread>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <signal.h>
#include "imgsearch.h"
#define PORT "8675"
#define BACKLOG 10
JNIEnv* create_vm() {
JavaVM* j;
JNIEnv* e;
JavaVMInitArgs j_args;
JavaVMOption opts;
j_args.version = JNI_VERSION_1_6;
j_args.nOptions = 1;
j_args.ignoreUnrecognized = 0;
char* cp = (char *)"-Djava.class.path=.:./lucene-4.4.0/analysis/common/lucene-analyzers-common-4.4.0.jar:./lucene-4.4.0/core/lucene-core-4.4.0.jar:./lucene-4.4.0/queryparser/lucene-queryparser-4.4.0.jar";
opts.optionString = cp;
j_args.options = &opts;
JNI_CreateJavaVM(&j, (void**)&e, &j_args);
return e;
}
JNIEnv* env = create_vm();
JavaVM* jvm;
void sigchld_handler(int s)
{
while(waitpid(-1, NULL, WNOHANG) > 0);
}
// get sockaddr, IPv4 or IPv6:
void *get_in_addr(struct sockaddr *sa)
{
if (sa->sa_family == AF_INET) {
return &(((struct sockaddr_in*)sa)->sin_addr);
}
return &(((struct sockaddr_in6*)sa)->sin6_addr);
}
void sockcom(int csock) {
std::cout << "Yeah!" << std::endl;
}
int main(int argc, char *argv[]) {
env->GetJavaVM(&jvm);
std::string fP = "features/esp.feature";
ImgSearch *iS;
iS = new ImgSearch();
iS->LoadFeatures(fP);
//iS->BuildClusters(150000, 15);
//iS->ClustersToFile("output.clusters");
iS->FileToClusts("output.clusters");
iS->BuildIndex();
//iS->IndexToFile("output.index");
//iS->FileToIndex("output.index");
//iS->FindNeighbors();
//iS->NeighborsToFile("output.neighbors");
iS->FileToNeighbors("output.neighbors");
iS->BagOfWords("features/imglist.txt", "features/esp.size", "coll/output.bagofwords");
iS->BuildLuceneIndex("coll/output.bagofwords");
//std::string queryfile = "queries/query1.pgm";
int sockfd, new_fd; // listen on sock_fd, new connection on new_fd
struct addrinfo hints, *servinfo, *p;
struct sockaddr_storage their_addr; // connector's address information
socklen_t sin_size;
struct sigaction sa;
int yes=1;
char s[INET6_ADDRSTRLEN];
int rv;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE; // use my IP
if ((rv = getaddrinfo(NULL, PORT, &hints, &servinfo)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
return 1;
}
// loop through all the results and bind to the first we can
for(p = servinfo; p != NULL; p = p->ai_next) {
if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) {
perror("server: socket");
continue;
}
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) {
perror("setsockopt");
exit(1);
}
if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
close(sockfd);
perror("server: bind");
continue;
}
break;
}
if (p == NULL) {
fprintf(stderr, "server: failed to bind\n");
return 2;
}
freeaddrinfo(servinfo); // all done with this structure
if (listen(sockfd, BACKLOG) == -1) {
perror("listen");
exit(1);
}
sa.sa_handler = sigchld_handler; // reap all dead processes
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
if (sigaction(SIGCHLD, &sa, NULL) == -1) { perror("sigaction"); exit(1); }
printf("server: waiting for connections...\n");
while(1) { // main accept() loop
int pipefd[2];
int pres = pipe(pipefd);
if (pres < 0) {
perror("pipe");
continue;
}
sin_size = sizeof their_addr;
new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size);
if (new_fd == -1) {
perror("accept");
continue;
}
inet_ntop(their_addr.ss_family, get_in_addr((struct sockaddr *)&their_addr), s, sizeof s);
printf("server: got connection from %s\n", s);
std::thread th(sockcom, new_fd);
th.detach();
std::string queryfile = "queries/query3.pgm";
iS->Search(queryfile);
}
jvm->DestroyJavaVM();
return 0;
}
So iS->Search(queryfile); basically is the following:
void ImgSearch::Search(std::string &filePath) {
ImgQuery newQuery(filePath, Ndx, WORDS);
newQuery.ExtractKeyPoints();
newQuery.FindNeighbors();
newQuery.BagOfWords();
newQuery.LuceneSearch();
return;
// Error Checking?
}
Where the LuceneSearch function is the only one that uses the JNI JVM.
void ImgQuery::LuceneSearch() {
jclass qcls = env->FindClass("BatchSearch");
if (qcls == NULL) std::cout << "BatchSearch: Not Found." << std::endl;
jmethodID qmid = env->GetStaticMethodID(qcls, "main", "()V");
env->CallStaticVoidMethod(qcls, qmid);
if (env->ExceptionCheck()) {
env->ExceptionDescribe();
}
std::cout << "Still working..." << std::endl;
return;
// Error Checking?
}
Basically, what I am wondering is A) do you see anything obviously wrong? and B) How do you properly reuse a JVM throughout a project?
EDIT: Is there a way to wait for a JVM to be ready?

JNI: Catching Init-Time Exceptions

Okay, I'm all out of ideas on this one. Does anyone have any idea how I can hook into Java's exception pipeline in order to catch (and log to a text file) all exceptions that are occurring?
The situation is this: I have a library in a JAR file (A) which in turn depends on a second JAR file (B). A has no main class, as it's simply a class library, which I'm accessing and invoking through the JNI. The problem I'm having is this. When I attempt to initialise the JNI with A loaded, the JNI returns an unspecified error.
I strongly suspect that this error originates from an instantiation of Log4J's logger unit, which is occurring in static code (outside of a method) in B, which I believe is throwing an IOException as a result of permissions problems on the log file. I'm having issues finding out what's going on, however, as the exception (which I suspect is the cause of the problem) is being thrown during the linking stage (when A imports B) and so cannot be caught by a try-catch block. Also, since there is no main method there is no obvious place to put a try-catch block in order to catch this exception.
I would like some way of catching all exceptions that arise in either JAR and dumping them into a text file. I cannot (easily) modify B (I do not have the decompiled JAR). Any ideas?
Here is the C code which invokes the JNI with the specified libraries and options:
_DLL_EXPORT PyObject *initVM(PyObject *self, PyObject *args, PyObject *kwds)
{
static char *kwnames[] = {
"classpath", "initialheap", "maxheap", "maxstack",
"vmargs", NULL
};
char *classpath = NULL;
char *initialheap = NULL, *maxheap = NULL, *maxstack = NULL;
char *vmargs = NULL;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|zzzzz", kwnames,
&classpath,
&initialheap, &maxheap, &maxstack,
&vmargs))
return NULL;
if (env->vm)
{
PyObject *module_cp = NULL;
if (initialheap || maxheap || maxstack || vmargs)
{
PyErr_SetString(PyExc_ValueError,
"JVM is already running, options are ineffective");
return NULL;
}
if (classpath == NULL && self != NULL)
{
module_cp = PyObject_GetAttrString(self, "CLASSPATH");
if (module_cp != NULL)
classpath = PyString_AsString(module_cp);
}
if (classpath && classpath[0])
env->setClassPath(classpath);
Py_XDECREF(module_cp);
return getVMEnv(self);
}
else
{
JavaVMInitArgs vm_args;
JavaVMOption vm_options[32];
JNIEnv *vm_env;
JavaVM *vm;
unsigned int nOptions = 0;
PyObject *module_cp = NULL;
vm_args.version = JNI_VERSION_1_4;
JNI_GetDefaultJavaVMInitArgs(&vm_args);
if (classpath == NULL && self != NULL)
{
module_cp = PyObject_GetAttrString(self, "CLASSPATH");
if (module_cp != NULL)
classpath = PyString_AsString(module_cp);
}
#ifdef _jcc_lib
PyObject *jcc = PyImport_ImportModule("jcc");
PyObject *cp = PyObject_GetAttrString(jcc, "CLASSPATH");
if (classpath)
add_paths("-Djava.class.path=", PyString_AsString(cp), classpath,
&vm_options[nOptions++]);
else
add_option("-Djava.class.path=", PyString_AsString(cp),
&vm_options[nOptions++]);
Py_DECREF(cp);
Py_DECREF(jcc);
#else
if (classpath)
add_option("-Djava.class.path=", classpath,
&vm_options[nOptions++]);
#endif
Py_XDECREF(module_cp);
if (initialheap)
add_option("-Xms", initialheap, &vm_options[nOptions++]);
if (maxheap)
add_option("-Xmx", maxheap, &vm_options[nOptions++]);
if (maxstack)
add_option("-Xss", maxstack, &vm_options[nOptions++]);
if (vmargs)
{
#ifdef _MSC_VER
char *buf = _strdup(vmargs);
#else
char *buf = strdup(vmargs);
#endif
char *sep = ",";
char *option;
for (option = strtok(buf, sep); option; option = strtok(NULL, sep))
{
if (nOptions < sizeof(vm_options) / sizeof(JavaVMOption))
add_option("", option, &vm_options[nOptions++]);
else
{
free(buf);
for (unsigned int i = 0; i < nOptions; i++)
delete vm_options[i].optionString;
PyErr_Format(PyExc_ValueError, "Too many options (> %d)",
nOptions);
return NULL;
}
}
free(buf);
}
//vm_options[nOptions++].optionString = "-verbose:gc";
//vm_options[nOptions++].optionString = "-Xcheck:jni";
vm_args.nOptions = nOptions;
vm_args.ignoreUnrecognized = JNI_FALSE;
vm_args.options = vm_options;
if (JNI_CreateJavaVM(&vm, (void **) &vm_env, &vm_args) < 0)
{
for (unsigned int i = 0; i < nOptions; i++)
delete vm_options[i].optionString;
PyErr_Format(PyExc_ValueError,
"An error occurred while creating Java VM");
return NULL;
}
env->set_vm(vm, vm_env);
for (unsigned int i = 0; i < nOptions; i++)
delete vm_options[i].optionString;
t_jccenv *jccenv = (t_jccenv *) PY_TYPE(JCCEnv).tp_alloc(&PY_TYPE(JCCEnv), 0);
jccenv->env = env;
#ifdef _jcc_lib
registerNatives(vm_env);
#endif
return (PyObject *) jccenv;
}
}
Okay, so I've got the solution I was after. The solution is an update to the following segment of the code listed in the question:
if (JNI_CreateJavaVM(&vm, (void **) &vm_env, &vm_args) < 0)
{
for (unsigned int i = 0; i < nOptions; i++)
delete vm_options[i].optionString;
PyErr_Format(PyExc_ValueError,
"An error occurred while creating Java VM");
return NULL;
}
The adaptation supports the construction of a more detailed error message which adds two specific pieces of information:
The error code (if any) which is returned by the JNI_CreateJavaVM method;
The detailed Java exception which occurs in the event that such an error code arises.
The above snippet from the original code was replaced with the following:
vmInitSuccess = JNI_CreateJavaVM(&vm, (void **) &vm_env, &vm_args);
if (vmInitSuccess < 0)
{
for (unsigned int i = 0; i < nOptions; i++)
delete vm_options[i].optionString;
//Set up basic error message
sprintf(strVMInitSuccess, "%d", vmInitSuccess);
strcpy(strVMError, "An error occurred while creating Java VM (No Exception): ");
strcat(strVMError, strVMInitSuccess);
//Get exception if there is one
if((exc = vm_env->ExceptionOccurred()))
{
//Clear the exception since we have it now
vm_env->ExceptionClear();
//Get the getMessage() method
if ((java_class = vm_env->FindClass ("java/lang/Throwable")))
{
if ((method = vm_env->GetMethodID(java_class, "getMessage", "()Ljava/lang/String;")))
{
int size;
strExc = static_cast<jstring>(vm_env->CallObjectMethod(exc, method));
charExc = vm_env->GetStringUTFChars(strExc, NULL);
size = sizeof(strVMError) + sizeof(charExc);
char strVMException[size];
strcpy(strVMException, "An error occurred while creating Java VM (Exception): ");
strcat(strVMException, charExc);
PyErr_Format(PyExc_ValueError, strVMException);
return NULL;
}
}
}
PyErr_Format(PyExc_ValueError, strVMError);
return NULL;
}
Thanks to #Parsifal for help with this solution.

Categories