The Java TM Tutorial

| Start of Tutorial > Start of Trail > Start of Lesson | Search

Trail: Java Native Interface
Lesson: Interacting with Java from the Native Side

## Invoking the Java Virtual Machine

> The JDK1.1 ships the Java Virtual Machine as a shared library (or dynamic link library on Win32). You can embed the Java Virtual Machine into your native application by linking the native application with the shared library. The JNI supports an Invocation API that allows you to load, initialize, and invoke the Java Virtual Machine. Indeed, the normal way of starting the Java programming language interpreter, `java`, is no more than a simple C program that parses the command line arguments and invokes the Java Virtual Machine through the Invocation API.

### Invoking the Java Virtual Machine

To illustrate, we will write a C program that invokes the Java Virtual Machine and calls the `Prog.main` method defined in `Prog.java`:

    public class Prog {
     public static void main(String[] args) {
      System.out.println("Hello World" + args[0]);
     }
    }

The C code in `invoke.c` begins with a call to `JNI_GetDefaultJavaVMInitArgs` to obtain the default initialization settings, such as heap size, stack size, and so on. It then calls `JNI_CreateJavaVM` to load and initialize the Virtual Machine. `JNI_CreateJavaVM` fills in two return values:

- `jvm` refers to the created Java Virtual Machine. You can use this to destroy the Virtual Machine at a later time, for example.
- `env` is a JNI interface pointer that the current thread can use to access Java programming language features, such as calling a Java programming language method.

Note that after `JNI_CreateJavaVM` successfully returns, the current native thread has bootstrapped itself into the Java Virtual Machine and is therefore running just like a native method. The only difference is that there is no concept of returning to the Java Virtual Machine. Therefore, any local references that you subsequently create will not be freed until you call `DestroyJavaVM`.

Once you have created the Java Virtual Machine, you can issue regular JNI calls to invoke, for example, `Prog.main`. `DestroyJavaVM` attempts to unload the Java Virtual Machine. (The JDK 1.1 Java Virtual Machine cannot be unloaded; therefore DestroyJavaVM always returns an error code.)

You need to compile and link `invoke.c` with the Java programming language libraries shipped with JDK1.1. On Solaris, you can use the following command to compile and link `invoke.c`:

    cc -I<where jni.h is> -L<where libjava.so is> -ljava invoke.c

On Win32 with Microsoft Visual C++ 4.0, the command line is:

    cl -I<where jni.h is> -MT invoke.c -link <where javai.lib is>\javai.lib

Those working in the MacOS environment should refer to the JManager API, which is part of the MacOS Runtime for Java (MRJ). You use the JManager API to embed Java applications into your application.

Run the resulting executable from the command line. It is possible that you may get the following error message:

    Unable to initialize threads: cannot find class java/lang/Thread
    Can't create Java VM

This error message indicates that you have set the wrong value for the `vm_args.classpath` variable.

You might get a different system error indicating that it cannot find either `libjava.so` (on Solaris) or `javai.dll` (on Win32). If this is the case, add `libjava.so` into your `LD_LIBRARY_PATH` on Solaris, or add `javai.dll` into your executable path on Win32.

The program may report an error that it cannot find the class `Prog`. If so, make sure the directory containing `Prog.class` is in the `vm_args.classpath` variable as well.

### Attaching Native Threads

The Invocation API also allows you to attach native threads to a running Java Virtual Machine and have the threads bootstrap themselves into Java programming language threads. This requires that the Java Virtual Machine internally uses native threads. In JDK 1.1, this feature only works on Win32. The Solaris version of Java Virtual Machine uses user-level thread support and is therefore incapable of attaching native threads. In the future, the JDK on Solaris will support native threads.

Our example program, `attach.c`, therefore, will only work on Win32. This example program is a variation of `invoke.c`. Instead of calling`Prog.main` in the main thread, the native code spawns five threads and then waits for them to finish before it destroys the Java Virtual Machine. Each thread attaches itself to the Java Virtual Machine, invokes the `Prog.main` method, and finally detaches itself from the Virtual Machine before it terminates. Note that the third argument to `AttachCurrentThread` is reserved and should be set to `NULL`.

When you call `DetachCurrentThread`, you free all local references belonging to the current thread.

### Limitations of the Invocation API in JDK1.1

As mentioned above, there are a number of limitations of the Invocation API implementation in JDK1.1.

- The user-level Java programming language thread implementation on Solaris requires the Java Virtual Machine to redirect certain Solaris system calls. The set of redirected system calls currently includes `read`, `readv`, `write`, `writev`, `getmsg`, `putmsg`, `poll`, `open`, `close`, `pipe`, `fcntl`, `dup`, `create`, `accept`, `recv`, `send`, and so on. This may cause undesirable effects on a hosting native application that also depends on these system calls.
- You cannot attach a native thread to the user-thread based Java Virtual Machine on Solaris. `AttachCurrentThread` simply fails on Solaris (unless your code calls it from the main thread that created the Virtual Machine).
- You cannot unload the Java Virtual Machine without terminating the process. The `DestroyJavaVM` call simply returns an error code.

These problems will be fixed in future releases of the JDK.

| Start of Tutorial > Start of Trail > Start of Lesson | Search