Invocar a la Máquina Virtual Java

En el JDK 1.1, la Máquina Virtual Java está almacenada como una librería compartida (o librería de enlace dinámico en Win32). Podemos introducir la VM en nuestra aplicación nativa, enlazando la aplicación con la librería. El JNI soporta un API de "Invocation" invocación que permite cargar, inicializar, y llamar a la Máquina Virtual Java. De hecho, la forma normal de arrancar el intérprete Java, java, no es más que un sencillo programa C que analiza los argumentos de la linéa de comandos y llama a la Máquina Virtual Java a través del API Invocation.

Llamar a la Máquina Virtual Java

Para ilustrar como llamar a la Máquina Virtual Java, escribiremos un programa C que la invoque y llame al método Prog.main definido en Prog.java:
public class Prog {
 public static void main(String[] args) {
  System.out.println("Hello World" + args[0]);
 }
}
El código C de invoke.c empieza con una llamada a JNI_GetDefaultJavaVMInitArgs para obtener losa valores para la inicialización por defecto (tamaño de la pila, etc) Luego llama a JNI_CreateJavaVM para cargar e inicializar la Máquina Virtual. JNI_CreateJavaVM rellena dos valores de retorno: Observa que después de que JNI_CreateJavaVM haya retornado satisfactoriamente, el thread nativo, se introduce a sí mismo en la Máquina Virtual Java y por lo tanto se está ejecutando como si fuera un método nativo. La única diferencia es que no existe el concepto de retorno de la Máquina Virtual Java. Por lo tanto, las referencias locales creadas después no serán liberadas hasta que se llame a DestroyJavaVM.

Una vez que se ha creado la Máquina Virtual Java, se pueden realizar llamadas normales del JNI, por ejemplo a Prog.main. DestroyJavaVM intenta descargar la MV. (En el JDK 1.1 la Máquina Virtual Java no puede ser descargada; por lo tanto DestroyJavaVM siempre devuelve un código de error.)

Se necesita compilar y enlazar invoke.c con las librerías Java distribuidas con el JDK 1.1. En Solaris, se puede utilizar el siguiente comando para compilar y enlazar invoke.c:

cc -I<where jni.h is> -L<where libjava.so is> -ljava invoke.c
En Win32 con Microsgt Visual C++ 4.0, la línea de comandos es:
cl -I<where jni.h is> -MT invoke.c -link <where javai.lib is>\javai.lib
Aquellos que trabajen en el entorno MacOS deberán referirse al API JManager, que forma parte del MacOS Runtime for Java (MRJ). Se utiliza este API para incluir aplicaciones Java en aplicaciones MAC.

Al ejecutar el programa resultante desde la línea de comandos, es posible que obtengamos el siguiente mensaje de error:

Unable to initialize threads: cannot find class java/lang/Thread
Can't create Java VM
Este mensaje de error indica que se ha seleccionado erróneamente el valor para la variable vm_args.classpath. Por otro lado, si el error indica que no puede encontrar libjava.so (en Solaris) o javai.dll (en Win32), añadiremos libjava.so a nuestro LD_LIBRARY_PATH en Solaris, o javai.dll en nuestro path de ejecutables en Win32. Si el programa muestra un error diciendo que no puede encontrar la clase Prog, debemos asegurarnos de que el directorio que contiene Prog.class también está en la variable vm_args.classpath.

Añadir Threads Nativos

El API Invocation también permite añadir threads nativos a una VM Java que se esté ejecutando y convertir los propios threads en threads Java. Esto requiere que la Máquina Virtual Java utilice internamente threads nativos. En el JDK 1.1, esta caracteristica sólo funciona en Win32. La versión Solaris de la Máquina Virtual Java utiliza soporte de threads de nivel de usuario y por lo tanto es incapaz de añadir threads nativos. Una futura versión del JDK para solaris soportará threads nativos.

Por lo tanto, nuestro programa de ejemplo, attach.c, sólo funcionará en Win32. Es una variación de invoke.c. En lugar de llamar a Prog.main en el thread principal, el código nativo espande cinco threads y espera a que finalicen antes de destruir la Máquina Virtual Java. Cada thread se añade a sí mismo a la Máquina Virtual Java, llama al método Prog.main, y finalmente se borra de la Máquina Virtual antes de terminar. Observa que el tercer argumento de AttachCurrentThread está reservado y siempre debe ser NULL.

Cuando se llama a DetachCurrentThread, todas las referencias locales pertenecientes al thread actual serán liberadas.

Limitaciones del API Invocation en el JDK 1.1

Como se mencionó anteriormente, existe un número de limitaciones en la implementación del API Invocation en el JDK 1.1. Estos problemas serán corregidos en futuras versiones del JDK.

Ozito