» Back to Appendix table of contents
» Back to Programmer's Guide table of contents

Table of contents
» Dynamically loading the classic JRE
 » Sample dynamically loading the Classic JRE with shl_load
» Debugging a native JNI library with Java™ 2 Hotspot VM under gdb

Dynamically loading the Classic JRE

Normally you would link against the JRE and use HotSpot, but sometimes you might want to load the JVM dynamically, especially if you are integrating Java™ with legacy applications.

Below is an example of how to dynamically load the Classic JRE from your C or C++ program without linking against it and starting to execute java code. When you do it this way you must dynamically retrieve the function pointers to the invocation API functions JNI_GetDefaultJavaVMInitArgs and JNI_CreateJavaVM. This is done with the shl* function in HP-UX. This example is based on the programs shown in the previous sections.

Sample Dynamically loading the Classic JRE with shl_load
Here we use the older shl_load() style API to load the JRE. Using BIND_VERBOSE in the flags is helpful during debugging. If an error occurs during loading, a message will appear in the console.

#include  <jni.h>
#include  <dl.h>
	
extern shl_t shl_load (const char *path, int flags, long address);
	
typedef void ( *VoidVoidProcPtr ) ( void );
typedef void ( *JNI_GetDefaultJavaVMInitArgs_ptr )( JDK1_1InitArgs* );
typedef void ( *JNI_CreateJavaVM_ptr )( JavaVM*, JNIEnv*, JDK1_1InitArgs* );
	
JNI_GetDefaultJavaVMInitArgs_ptr        gdjvmiai = 0;
JNI_CreateJavaVM_ptr                    cjvm = 0;
	
void load_java ( void )
{
	shl_t javalib = {0};
	int result      = 0;
	void *foundSym  = NULL;
	javalib = shl_load( "/opt/java1.3/jre/lib/PA_RISC\
			/classic/libjvm.sl",
			BIND_IMMEDIATE | BIND_VERBOSE,
			NULL );
	
	if( !javalib ) {
			abort();
	}
	result = shl_findsym( &javalib, \
	"JNI_GetDefaultJavaVMInitArgs", TYPE_PROCEDURE, \
	&foundSym );
	if( result ) {
			abort();
	}
	gdjvmiai = foundSym;
	foundSym = NULL;
	
	result = shl_findsym( &javalib, \
	"JNI_CreateJavaVM", TYPE_PROCEDURE, &foundSym );
	if( result ) {
			abort();
	}
	cjvm = foundSym;
	
}
	
main() {
	JavaVM *jvm;
	JNIEnv *env;
	JDK1_1InitArgs vm_args;
	
	 const struct JNINativeInterface_ *jni;
	 const struct JNIInvokeInterface_ *vmi;
	
	 jclass cls;
	 jmethodID mid;
	
	 printf("loading jvm...\n");
	
		load_java();
	
	 printf("beginning execution...\n");
	
	 vm_args.version = 0x00010001;

At this point you could call JNI_GetDefaultJavaVMInitArgs and JNI_CreateJavaVM if you were linking against libjvm.sl, but here it's all dynamic, so you call it through a function pointer.

/* Get the default initialization args and set the class path */
/*
JNI_GetDefaultJavaVMInitArgs(&vm_args);
*/
( *gdjvmiai )( &vm_args );
	
	
/*
* Note if you have not set and exported the environment variable
* CLASSPATH you may also need to add this:
*/
vm_args.classpath = "/opt/java1.3/jre/lib/rt.jar:.";
	
/* create and initialize a new Java VM,
* return a JNI interface ptr in env
*/
/* JNI_CreateJavaVM(&jvm,&env,&vm_args);
*/
	( *cjvm )( &jvm,&env,&vm_args );
	
	jni = *env;
	vmi = *jvm;
	
/* Find the class */
	cls = jni->FindClass(env, "TestNonJavaCallingJava");
	if (cls == 0) {
		printf("Could not locate class TestNonJavaCallingJava"
		" in your CLASSPATH.\n");
	 exit(1);
	}
/* Find the method */
	mid = jni->GetStaticMethodID(env, cls, "printInt", "(I)V");
	if (mid == 0) {
		printf("Could not locate method printInt with signature (I)V"
		" in the class TestNonJavaCallingJava.\n");
	exit(1);
	}
	
/* Invoke the method */
	jni->CallStaticVoidMethod(env, cls, mid, 100);
	
/* we are done */
	vmi->DestroyJavaVM(jvm);
	
}

To compile and link this program do the following:

cc -z -g +u4 -c -Ae -I/opt/java1.3/include -I/opt/java1.3/include/HP-UX \
-D_HPUX -DNATIVE -D_POSIX_C_SOURCE=199506L c_main.c cc -g -o c_main c_main.o -lcl \
-lpthread -llwp

Note there are no Java™ libraries included on the link line. Ordinarily the Java™ launch script sets up the SHLIB_PATH, but with a C main, you must do it yourself, like this.

%
% setenv SHLIB_PATH /opt/java1.3/jre/lib/PA_RISC/:\
/opt/java1.3/jre/lib/PA_RISCnative_threads: /opt/java1.3/jre/lib/PA_RISC/classic
%
% ./c_main
loading jvm...
beginning execution...
TestNonJavaCallingJava.printInt recieved: 100
%

Debugging a native JNI library with Java™ 2 Hotspot VM under gdb

Below is a method for debugging a library in the Java™ SDK 1.2 JVM running in "classic" mode.

First set the $SHLIB_PATH and the $CLASSPATH since Java™ won't be starting from its usual script. Remember to include the location of the user JNI library in the $SHLIB_PATH.

In csh:

% setenv SHLIB_PATH /opt/java1.2/jre/lib/PA_RISC/:       \
/opt/java1.2/jre/lib/PA_RISC/classic/:.
% setenv CLASSPATH /opt/java1.2/jre/lib/rt.jar:.

In ksh:

$ export SHLIB_PATH=/opt/java1.2/jre/lib/PA_RISC/:       \
/opt/java1.2/jre/lib/PA_RISC/classic/:.
$ export CLASSPATH=/opt/java1.2/jre/lib/rt.jar:.

Now start gdb as below with the real java command in /opt/java1.2/jre/bin/PA_RISC/native_threads/java. (The java command found in /opt/java1.2/bin/ is a script which calls this one.)

% /opt/langtools/bin/gdb      \
/opt/java1.2/jre/bin/PA_RISC/native_threads/java
HP gdb D49 Beta2.0
Copyright 1986 - 1999 Free Software Foundation, Inc.
Hewlett-Packard Wildebeest D49 Beta2.0 (based on 
GDB 4.17-hpwdb-980821)
Wildebeest is free software, covered by the GNU General Public 
License, and you are welcome to change it and/or distribute 
copies of it under certain conditions.  Type "show copying" 
to see the conditions.  There is absolutely no warranty for 
Wildebeest.
Type "show warranty" for details.
Wildebeest was built for PA-RISC 1.1 or 2.0 (narrow), HP-UX 11.00.
This version of gdb sends mail to log beta testing.
(no debugging symbols found)...
(gdb)

When the preceding message comes up, set a breakpoint at main. The Java™ program is called ArrayTest, and it has a native JNI library called libWaiterThread.sl. The library has a native function called Java_WaiterThread_PinForAWhile.

Start Java™ running, passing the name of the Java™ program, and any other Java™ parameters you wish
(-verbosegc, etc...)

(gdb) break main
Breakpoint 1 at 0x2e64
(gdb) r -classic ArrayTest
Starting program: /opt/java1.2/jre/bin/PA_RISC/native_threads/java
-classic ArrayTest
(no debugging symbols found)...(no debugging symbols found)...
(no debugging symbols found)...(no debugging symbols found)...
(no debugging symbols found)...(no debugging symbols found)...
(no debugging symbols found)...(no debugging symbols found)...
(no debugging symbols found)...(no debugging symbols found)...
Breakpoint 1, 0x2e64 in main ()
(gdb) 

To make sure you got Classic and not Hotspot, break at JNI_CreateJavaVM to see which got loaded, as follows:

(gdb) break JNI_CreateJavaVM Breakpoint 2 at 0x7ac76574
(gdb) c
Continuing.
Breakpoint 2, 0x7ac76574 in JNI_CreateJavaVM () from     \
/opt/java1.2/jre/lib/PA_RISC/classic//libjvm.sl
(gdb)

Once this happens, the Java™ shared library is loaded, so you can set a breakpoint at the routine sysLoadLibrary, which actually loads the user shared library.

(gdb) break sysLoadLibrary
Breakpoint 3 at 0x7ac767a8
(gdb) c
Continuing.
[New thread 2 (system thread 3330)]
Breakpoint 3, 0x7ac767a8 in sysLoadLibrary () from 
/opt/java1.2/jre/lib/PA_RISC/classic//libjvm.sl
(gdb) x/s $r26
0x7afe1038:      "/opt/java1.2/jre/lib/PA_RISC/libjava.sl"
(gdb) c
Continuing.
(no debugging symbols found)...
Breakpoint 3, 0x7ac767a8 in sysLoadLibrary () from     \
	/opt/java1.2/jre/lib/PA_RISC/classic//libjvm.sl
(gdb) x/s $r26
0x7afe1058:      "/opt/java1.2/jre/lib/PA_RISC/libzip.sl"
(gdb) c
Continuing.
(no debugging symbols found)...[New thread 3 (system thread 3332)]
[New thread 4 (system thread 3333)] [New thread 5 (system thread 3334)]
[New thread 6 (system thread 3335)]
Breakpoint 3, 0x7ac767a8 in sysLoadLibrary () from     \
	/opt/java1.2/jre/lib/PA_RISC/classic//libjvm.sl
(gdb) x/s $r26
0x40246518:      "/opt/java1.2/jre/lib/PA_RISC/libzip.sl"
(gdb) c
Continuing.
Breakpoint 3, 0x7ac767a8 in sysLoadLibrary () from \  
	/opt/java1.2/jre/lib/PA_RISC/classic//libjvm.sl
(gdb) x/s $r26
0x402b91a0:      "/tmp_mnt/home/ecaspole/ecaspole/bugs\ 
	/JNI/libWaiterThread.sl"

In $r26 you can see if you are loading the library you care about. If the library is the right one, use the "finish" command to allow this routine to complete and load the library.

(gdb) finish
Run till exit from #0  0x7ac767a8 in sysLoadLibrary () from    \
	/opt/java1.2/jre/lib/PA_RISC/classic//libjvm.sl
0x7ad5d1d8 in JVM_LoadLibrary () from     \
	/opt/java1.2/jre/lib/PA_RISC/classic//libjvm.sl

When this is done, you now have access to the functions in the user JNI library. Here you can see that you can set a breakpoint at your native method. Then you continue, and eventually it stops at your native method. You have the source for your native library in the same directory, so it appears here.

(gdb) break Java_WaiterThread_PinForAWhile
Breakpoint 4 at 0x7a8f80bc: file ArrayTest.C, line 23.
(gdb) c
Continuing.
[New thread 7 (system thread 3336)]
[Switched to thread 7 (system thread 3336)]
Breakpoint 4, Java_WaiterThread_PinForAWhile (env=0x402bc428,
that=0x7a8d53ec, pinString=0x402bd7f0) at ArrayTest.C:23
23          jlong       sleepCount      = 500;
Current language:  auto; currently c++
(gdb) l
18                                    jstring pinString  )
19 {
20     jclass      serverClass;
21     jmethodID   sleepMethodID;
22     const jchar *pinnedChars;
23     jlong       sleepCount      = 500;
24     long        loopCount       = 100000;
25     serverClass = env -> GetObjectClass ( that );
26     sleepMethodID = env -> GetStaticMethodID ( serverClass,
27                                          "sleep", "(J)V" );

Now you are stopped in your native method.

» Please let us know additional information you'd like to see in the programmer's guide.

Java™ and all Java-based trademarks and logos are trademarks or registered trademarks of Sun Microsystems, Inc. in the U.S. and other countries. Hewlett-Packard is independent of Sun Microsystems, Inc.