\ vm_args.version = 0x00010002; vm_args.options = options; vm_args.nOptions = 1;
vm_args.ignoreUnrecognized = JNI_TRUE; /* Create the Java VM */
res = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args); #else
JDK1_1InitArgs vm_args; char classpath[1024];
vm_args.version = 0x00010001;
JNI_GetDefaultJavaVMInitArgs(&vm_args);
/* Append USER_CLASSPATH to the default system class path */ sprintf(classpath, \
vm_args.classpath, PATH_SEPARATOR, USER_CLASSPATH); vm_args.classpath = classpath; /* Create the Java VM */
res = JNI_CreateJavaVM(&jvm, &env, &vm_args); #endif /* JNI_VERSION_1_2 */
if (res < 0) {
fprintf(stderr, \ exit(1); }
cls = (*env)->FindClass(env, \ if (cls == NULL) { goto destroy; }
mid = (*env)->GetStaticMethodID(env, cls, \
\ if (mid == NULL) { goto destroy; }
jstr = (*env)->NewStringUTF(env, \ if (jstr == NULL) { goto destroy; }
stringClass = (*env)->FindClass(env, \ args = (*env)->NewObjectArray(env, 1, stringClass, jstr); if (args == NULL) { goto destroy; }
(*env)->CallStaticVoidMethod(env, cls, mid, args);
destroy:
if ((*env)->ExceptionOccurred(env)) { (*env)->ExceptionDescribe(env); }
(*jvm)->DestroyJavaVM(jvm); }
上面的代码有条件地编译一个初始化JDK1_1InitArgs这个structure。这个structure是JDK1.1下特有的,尽管JDK1.2也会支持,但JDK1.2引入了一个更通用的叫作JavaVMInitArgs的VM初始化structure。
常量JNI_VERSION_1_2在JDK1.2下定义,JDK1.1下是没有的。
当目标平台是1.1时,C代码首先调用JNI_GetDefaultJavaVMInitArgs来获得默认的VM设置。这个调用会返回heap size、stack size、默认类路径等信息,并把这些信息存放在参数vm_args中。然后我们把Prog.class所在的目录附加到vm_args.classpath中。
当平台目标是1.2时,C代码创建了一个JavaVMInitArgs的structure。VM的初始化参数被存放在一个JavaVMOption数组中。
设置完VM初始化structure后,C程序调用JNI_CreateJavaVM来加载和初始化JVM,传入的前两个参数:
1、 接口指针jvm,指向新创建的JVM。 2、 当前线程的JNIEnv接口指针env。本地代码通过env指针访问
JNI函数。
当函数JNI_CreateJavaVM函数成功返回时,当前本地线程已经把自己的控制权交给JVM。这时,它会就像一个本地方法一样运行。以后就可以通过JNI函数来启动Prog.main方法。 接着,程序调用DestroyJavaVM函数来unloadJVM。不幸的是,在JDK1.1和JDK1.2中你不能unloadJVM,它会一直返回一个错误码。 运行上面的程序,产生如下输出: Hello World from C!
7.2 把本地程序和JVM链接在一起
通过调用接口,你可把invoke.c这样的程序和一个JVM链接到一起。怎么样链接JVM取决于本地程序是要和一个特定的VM一起工作,还是要和多个具体实现方式未知的不同VM一起工作。
7.2.1 和一个己知的JVM链接到一起
这种情况下,你可以把你的本地程序和实现了JVM的本地库链接在一起。编译链接成功后,你就可以运行得到的可执行文件。运行时,你可能会得到一个错误信息,比如“无法找到共享库或者动态链接库”,在Windows下,错误信息可能会指出无法发现动态链接库javai.dll(JDK1.1)或者jvm.dll(JDK1.2),这时,你需要把DLL文件加载到你的PATH环境变量中去。
7.2.2 和未知的多个JVM链接到一起
这种情况下,你就不能把本地程序直接和一个特定的库链接在一起了。比如,JDK1.1的库是javai.dll,而JDK1.2的库是jvm.dll。
解决方案是根据本地程序的需要,用运行时动态链接来加载不同的VM库。例如,下面的win32代码,根据给定的VM库的路径找到JNI_CreateJavaVM函数的入口。 LoadLibrary和GetProcAddress是Win32平台上用来动态链接的API。虽然LoadLibrary可以实现了JVM的本地库的名字(如“jvm”)或者路径(如
“C:\\\\jdk1.2\\\\jre\\\\bin\\\\classic\\\\jvm.dll”)。最好把本地库的绝对路径传递给JNU_FindCreateJavaVM,让LoadLibrary去搜索jvm.dll,这样程序就不怕环境变量被改变了。
7.3 附加本地线程
假设,你有一个用C写的服务器这样的多线程程序。当HTTP请求进来的时候,服务器创建许多本地线程来并行的处理HTTP请求。为了让多个线程可以同时操作JVM,我们可能需要把一个JVM植入这个服务器。
图7.1 把JVM嵌入WEB服务器
服务器上的本地方法的生命周期一般会比JVM要短,因此我们需要一个方法把本地线程附加到一个已经在运行的JVM上面,然后在这个本地方法中进行JNI调用,最后在不打扰其它连接到JVM上的线程的情况下把这个本地线程和JVM分离。 下面这个例子中,attach.c演示了怎么样使用调用接口(invocation interface)把本地线程附加到VM上去,这段程序使用的是Win32线程API。 /* Note: This program only works on Win32 */ #include
JavaVM *jvm; /* The virtual machine instance */
#define PATH_SEPARATOR ';'
#define USER_CLASSPATH \
void thread_fun(void *arg) {
jint res; jclass cls; jmethodID mid; jstring jstr;
jclass stringClass; jobjectArray args; JNIEnv *env; char buf[100];
int threadNum = (int)arg;
/* Pass NULL as the third argument */ #ifdef JNI_VERSION_1_2
res = (*jvm)->AttachCurrentThread(jvm, (void**)&env, NULL); #else
res = (*jvm)->AttachCurrentThread(jvm, &env, NULL); #endif
if (res < 0) {
fprintf(stderr, \ return; }
cls = (*env)->FindClass(env, \ if (cls == NULL) { goto detach; }
mid = (*env)->GetStaticMethodID(env, cls, \
\ if (mid == NULL) { goto detach; }
sprintf(buf, \ jstr = (*env)->NewStringUTF(env, buf); if (jstr == NULL) { goto detach; }
stringClass = (*env)->FindClass(env, \ args = (*env)->NewObjectArray(env, 1, stringClass, jstr); if (args == NULL) { goto detach; }
(*env)->CallStaticVoidMethod(env, cls, mid, args);
detach:
if ((*env)->ExceptionOccurred(env)) {
(*env)->ExceptionDescribe(env); }
(*jvm)->DetachCurrentThread(jvm); }
main() {
JNIEnv *env; int i; jint res;
#ifdef JNI_VERSION_1_2
JavaVMInitArgs vm_args; JavaVMOption options[1]; options[0].optionString =
\ vm_args.version = 0x00010002; vm_args.options = options; vm_args.nOptions = 1;
vm_args.ignoreUnrecognized = TRUE; /* Create the Java VM */
res = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args); #else
JDK1_1InitArgs vm_args; char classpath[1024];
vm_args.version = 0x00010001;
JNI_GetDefaultJavaVMInitArgs(&vm_args);
/* Append USER_CLASSPATH to the default system class path */ sprintf(classpath, \
vm_args.classpath, PATH_SEPARATOR, USER_CLASSPATH); vm_args.classpath = classpath; /* Create the Java VM */
res = JNI_CreateJavaVM(&jvm, &env, &vm_args); #endif /* JNI_VERSION_1_2 */
if (res < 0) {
fprintf(stderr, \ exit(1); }
for (i = 0; i < 5; i++)
/* We pass the thread number to every thread */ _beginthread(thread_fun, 0, (void *)i); Sleep(1000); /* wait for threads to start */ (*jvm)->DestroyJavaVM(jvm); }