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:
- jvm se refiere la Máquina Virtual creada. Se puede utilizar para destruir la Máquina Virtual, por ejemplo.
- env es un puntero a interface JNI que el thread actual puede utilizar para acceder a las caracterísitcas Java, como llamar a un método Java.
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.
- La implementación de thredas a nivel de usuario en Solaris requiere que la Máquina Virtual Java redireccione ciertas llamadas al sistema. El juego de llamadas redireccionadas realmente incluye: read, readv, write, writev, getmsg, putmsg, poll, open, close, pipe, fcntl, dup, create, accept,
recv, send, etc. Esto podría causar efectos indeseados en aplicaciones nativas, que también dependan de llamadas al sistema.
- No se pueden añadir threads narivos a la Máquina Virtual Java bajo Solaris. AttachCurrentThread simplemente falla en Solaris (a menos que se llame desde el thread principal que creó la Máquina Virtual).
- No se puede destruir la Máquina Virtual Java sin terminar el proceso. La llamada a DestroyJavaVM simplemente devuelve un código de error.
Estos problemas serán corregidos en futuras versiones del JDK.
Ozito