jump to navigation

JNI en Linux febrero 7, 2007

Posted by superpiwi in Java.
trackback

Vamos a ver un ejemplo de como escribir una libreria en C que pueda ser llamada desde nuestras clases en JAVA. Para ello emplearemos JNI (Java Native Interface). Para compilar la libreria empleamos CDT que es un subproyecto de Eclipse para tener un IDE para C/C++. Asi que sin salir del entorno podemos estar desarrollando código Java y escribiendo nuestras librerias en C/C++. Empezamos.

Escribimos la clase java:

//File: Hello.java
public class Hello
{
public native void sayHello();
static {
System.out.println("java.library.path:"+System.getProperty("java.library.path"));
// Si quisieramos cargar la libreria buscando en el java.library.path
//System.loadLibrary("hello");
// Para forzar la carga de la libreria
System.load("/home/jose/workspace/Hello/hello.so");
}
public static void main(String[] args) {
Hello h = new Hello();
h.sayHello ();
}
}

Si intentamos ejecutar la clase java obtenemos lo siguiente:

jose@soledad:~/workspace/Hello$ ls
Hello.class Hello.h Hello.java
jose@soledad:~/workspace/Hello$ java Hello
java.library.path:/usr/lib/jvm/java-1.5.0-sun-1.5.0.08/jre/lib/i386/client:/usr/lib/jvm/java-1.5.0-sun-1.5.0.08/jre/lib/i386:/usr/lib/jvm/java-1.5.0-sun-1.5.0.08/jre/../lib/i386:/home/jose/workspace/Hello
cargando /home/jose/workspace/Hello/hello.so
Exception in thread "main" java.lang.UnsatisfiedLinkError: Can't load library: /home/jose/workspace/Hello/hello.so
at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1650)
at java.lang.Runtime.load0(Runtime.java:769)
at java.lang.System.load(System.java:968)
at Hello.<clinit>(Hello.java:9)
jose@soledad:~/workspace/Hello$

Normal, porque intenta cargar la libreria de enlace dinamico y no existe. Vamos a crearla.

Para ello, ahora generamos de manera automatica la cabecera del fichero C de la libreria empleando la utilidad javah:
javah Hello

se genera un fichero Hello.h

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class Hello */
#ifndef _Included_Hello
#define _Included_Hello
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: Hello
* Method: sayHello
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_Hello_sayHello
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif

Ahora abrimos Eclipse para crear un nuevo Proyecto C mediante CDT:

File > New > Standard make C project

copiamos el fichero Hello.h y creamos el fichero Hello.c

//File: Hello.c
#include <jni.h>
#include "Hello.h"
#include <stdio.h>
JNIEXPORT void JNICALL Java_Hello_sayHello
(JNIEnv *env, jobject obj)
{
printf("Hello world!\n");
return;
}

jni01.png

Creamos tambien el makefile:

# All Target
all:
-@echo 'Generando libreria de enlace dinamico'
gcc -shared -I"/usr/lib/jvm/java-1.5.0-sun/include" -I"/usr/lib/jvm/java-1.5.0-sun/include/linux" -o hello.so Hello.c
# clean Target
clean:
-@echo 'Eliminando libreria de enlace dinamico hello.so'
-rm hello.so

jni02.jpg

En propiedades del proyecto añadimos los includes de las librerias de JNI (estas cuelgan del directorio donde hayamos instalado el JDK). En el ejemplo:

/usr/lib/jvm/java-1.5.0-sun/include
/usr/lib/jvm/java-1.5.0-sun/include/linux

jni03.jpg

Compilamos la libreria de enlace dinamico con la opcion de menu Project > Build project
Se genera el fichero hello.so que copiamos al directorio donde esta la clase java. Ejecutamos y ya está.


jose@soledad:~/workspace/JAVAJNI$ ls
Hello.c Hello.h hello.so LEEME.txt makefile
jose@soledad:~/workspace/JAVAJNI$ make clean
Eliminando libreria de enlace dinamico hello.so
rm hello.so
jose@soledad:~/workspace/JAVAJNI$ make
Generando libreria de enlace dinamico
gcc -shared -I"/usr/lib/jvm/java-1.5.0-sun/include" -I"/usr/lib/jvm/java-1.5.0-sun/include/linux" -o hello.so Hello.c
jose@soledad:~/workspace/JAVAJNI$ cp hello.so ../Hello
jose@soledad:~/workspace/JAVAJNI$ cd ../Hello
jose@soledad:~/workspace/Hello$ ls
Hello.class Hello.h Hello.java hello.so
jose@soledad:~/workspace/Hello$ java Hello
java.library.path:/usr/lib/jvm/java-1.5.0-sun-1.5.0.08/jre/lib/i386/client:/usr/lib/jvm/java-1.5.0-sun-1.5.0.08/jre/lib/i386:/usr/lib/jvm/java-1.5.0-sun-1.5.0.08/jre/../lib/i386:/home/jose/workspace/Hello
cargando /home/jose/workspace/Hello/hello.so
Hello world!
jose@soledad:~/workspace/Hello$

Comentarios»

1. Jacob - marzo 12, 2007

Hola,

Tengo un código parecido al tuyo, pero me gustaría cargar la librería utilizando LD_LIBRARY_PATH. He intentado, siguiendo varios tutoriales y ejemplos, incluido el tuyo, realizarlo sin éxito. Gracias a tu post he podido cargar la librería explicitamente y funciona, pero gustaría hacerlco con el método System.loadLibrary (el que comentas en tu codigo).

El problema que tengo es que me dice que no encuentra la librería a pesar que LD_LIBRARY_PATH esta correctamente establecida y en el java.library.path que se imprime antes de hacerlo aparece el path que yo he añadido y donde se encuentra la librería.

Sabes si me falta por hacer algo ? Has conseguido hacerlo tú ?

Muchas gracias

2. superpiwi - marzo 12, 2007

Pues me parece que a mi me daba el mismo problema que a ti, y llegue a pensar que era por alguna forma de compilar la DLL. El mismo ejemplo desde Windows si que me funcionaba con Mingw, pero desde Linux no lo consegui y no he seguido mirando (cuando pueda lo seguire intentando), tampoco le di mucha importancia porque lo cargue con el otro metodo pasando la ruta exacta de donde habia guardado la DLL.

3. EDarío - marzo 16, 2007

Hey, hola a todos. Antes de plantearles mi duda quisiera felicitarlos por éste mini-tutorial de cómo usar JNI, la verdad es que está bastante bueno, ¡muchas felicidades!

A lo que voy, ésta es mi duda: he seguido el ejemplo de JNI que se encuentra en el sitio http://pisuerga.inf.ubu.es/lsi/Invest/Java/Tuto/A_III.htm, el cual muestra lo mismo que aquí plantean, el punto es que logro hacer todo, la clase Java, el header file y el código C. La librería [DLL pues lo estoy trabajando desde Windows : ( ] la genero con la sigte. línea:
cl -LD HolaNativo.c y genera archivos HolaNativo (extensiones LIB, OBJ, EXP y DLL). Hasta aquí todo marcha bien, pero cuando ejecuto la clase HolaNativo recibo un mensaje de error:

Exception in thread «main» java.lang.UnsatisfiedLinkError: diHola
at HolaNativo.diHola(Native Method)
at HolaNativo.main(HolaNativo.java:25)

Esto es cuando en el método static de la clase cargo la librería con load(«ABSOLUTE_PATH») o con el método loadLibrary(«library»).

Para el caso de loadLibrary hasta copie la librería en {JAVA_HOME}\bin (que es una de las rutas donde busca las librerias) y ni aún así!

De verdad que necesito que me ayuden pues lo que estoy tratando de hacer es un algoritmo de encriptación (no mío, el algoritmo de Rjndail) pero necesito unos métodos en C y lo quiero programar en Java y cuando leí sobre JNI intuí que (quizás) me puede ayudar para el propósito.

Si desean aún más información para poder ayudarme les público el código fuente para que lo vean y me comenten si algo está mal. Por todo muchas gracias y nuevamente muchas felicidades!

4. superpiwi - marzo 16, 2007

Si, java.lang.UnsatisfiedLinkError es el tipico fallo que aparece cuando no te carga o encuentra la libreria. En windows me pasaba y entonces lo que hacia es colocar la .dll en alguno de los directorios donde se suele buscar, p.ej c:/windows/system, pero asegurate imprimiendo en tu codigo el contenido del java.library.path con System.getProperty(”java.library.path), asi puedes asegurarte de donde se estan buscando esas librerias. Mas cosas. El nombre, ahora mismo no recuerdo si tenias que pasarlo con la extension o no. P.ej si tu DLL se llama util.dll, creo que basta con hacer un System.load(«util») sin poner la extension, pero no lo recuerdo bien y no te lo puedo asegurar porque ya abandone el entorno Windows. De todas formas otra cosa te puede pasar es que no estes generando correctamente la DLL, se te genera el fichero pero no es valida. todo depende de los flags de compilacion. Te dejo el enlace de un tutorial de JNI para Windows que probe hace mucho tiempo y que me fue perfecto, Tal vez te ayude. Saludos.

http://www.inonit.com/cygwin/jni/helloWorld/

5. Gabriel Huerta Araujo - junio 25, 2007

Hola
Tengo un problema parecido. Solo que yo desarrollo en SUN(UNIX). Estoy desarrollando una aplicación con thread’s y haciendo uso de JNI. Tengo una clase que hace uso de «native functions». Antes de mandar ejecutar los thread’s la aplicación manda llamar uno de los métodos nativos(implementados en C++) y no marca error, pero una vez que inicia uno de los thread’s marca el siguiente error :
java.lang.UnsatisfiedLinkError: estarEnEjecucion
at MemoriaCompartida.estarEnEjecucion(Native Method)
at MemoriaCompartida.estarEnEjecucion(MemoriaCompartida.java:72)
at Censor.timerIntervalo(Censor.java:82)
at Timer.run(Timer.java:225)
at java.lang.Thread.run(Thread.java:534)

Pareciera que no encuentra el método será que tengo que volver a cargar la clase(System.load(«MemoriaCompartida»)???

De antemano, gracias por su apoyo. Y una disculpa por molestarles!!!

6. superpiwi - junio 27, 2007

Es interesante lo que comentas. la verdad que no lo he probado, en cuanto pueda intentare un ejemplillo para ver si me da el mismo error. De todas formas me imagino que en los threads llamas a tu clase y es tu clase la que llama al metodo nativo. Si conviertes esa clase a static o a Singleton, tal vez ya no te diera el problema. no se. Si veo algo ya te lo cuento.

7. Juan - agosto 7, 2007

Hola!

Saben el tema del UnstatisfiedLinkError en Unix linux quizas se deba a que tienen que renombrar la libreria ya compilada a libnombrelibreria.so

A mi me funciono asi luego de varios intentos, es una burrada, pero algo que hay que hacer

Comunmente uno pone System.loadLibrary(«nombrelibreria»), pero el archivo fisico debe tener el nombre que ya les describii

A mi no me funciona del todo algo complejo, pero los ejemplos simples si andan

Bueno a seguir revisando

Saludos y feliz programacion 😉

8. superpiwi - agosto 16, 2007

Muchas gracias Juan. lo probare 😉

9. Alf - agosto 21, 2007

Si tenemos:

System.loadLibrary(”hello”);

Entonces la libreria debe llamarse «libhello.so» y LD_LIBRARY_PATH debe contener el directorio donde reside

10. Diego Espoosito - agosto 24, 2007

Encontre la posta, debugueando la JDK, me di cuenta que java en linux le concatena por izquierda lib + nombre_libreria. Gracias a esto saque el lib de adelante y todo funciono bien, ya que de casualidad mi libreria se llama libPEPITO.
Saludos, esopero haberlos ayudado.

11. Jeannette - septiembre 13, 2007

Hola!

Yo también he intentado trabajar con JNI con los ejemplos más sencillos siguiendo los tutoriales bajo el entorno Linux. A pesar de ello, se aparece el error de:
java.lang.UnsatisfiedLinkError: no LibHola in java.library.path.
El Codigo esta al final del mensaje y he hecho lo dicho en los comentarios:
1.- colocar como nombre de libreria: libLibHola.so, ya que el loadLibrary llama LibHola.
2.- he colocado en la variable LD_LIBRARY_PATH el directorio donde se encuentra la libreria.
La sentencia que utilizo para compilar la libreria es:
gcc -fPIC -shared -o libLibHola.so HolaNativo.c

Estoy olvidando de hacer algo.
Cualquier ayuda se los agradecería un montón.
Un Saludo.

public class HolaNativo {

public native void diHola();

static {
System.loadLibrary(«LibHola»);
}

public static void main( String[] args ) {
// System.out.println(«Valor: » + System.getProperty(«java.library.path»));
new HolaNativo().diHola();
}
}

12. DarioAxel - noviembre 23, 2007

Hola,
Ese mismo problema tenia yo, pero lo solucione con;

System.load(«/home/urena/workspace/myTestJNI/src/unicap_jni.so»);

si uso System.loadLibrary(«unicap_jni»); y la libreria guardada en el directorio.

A todo esto, trabajo en linux como se podrá comprobar por el path. Ahora el problema que me encuentro es;
Can’t load IA 32-bit .so on a IA 32-bit platform

voy a ver si lo soluciono…

un saludo

13. rubi - octubre 24, 2008

hola, muchas gracias por todas sus aportaciones, me han sido de mucha utilidad, tengo un problemita, al compilar el makefile me marca un error dentro de la libreria jni.h, sabe alguien de ustedes como solucionarlo, no es por el path, esta correcto, los errores ke marca es dentro de la lib.

14. rubi - octubre 24, 2008

hola, de nuevo tengo una duda, y por fin pude compilar la libreria, solo ke al correr el programa me marca lo siguiente libtem.so :only ET_DYN and ET_EXEC can be loaded, alguien podria decirme ke significa eso?

15. miguelito - diciembre 12, 2008

hoa tengo un problema que no me deja crear la libreria dinamica el mensaje que me sale es el siguiente:
HolaMundo.c:16:17: error: jni.h: No existe el fichero o el directorio
In file included from HolaMundo.c:18:
estoy tratando de compilarlo solamente con gcc….sin eclipse….

Sera que alguien puede ayudarme…??

16. lunaticawpress - junio 12, 2009

Hola¡¡¡

Tengo un problema y es cuando intento ejecutar el comando javah -jni HolaMundo (Ejemplo)

En consola dice:

error: cannot access HolaMundo
class file por HolaMundo not found
javadoc: error – Class HolaMundo not found

Sin embargo yo estoy segura que en esa ruta se generan los .class…

Por favor,,, Ayuda¡¡¡ Es Urgente¡¡¡

Gracias LuNaTiCa

17. Jorge - octubre 19, 2009

Hola . excelente tutorial .probado en el linux y en windows y trabajo a la perfeccion .. mi duda es acerca de si puedo llamar codigo nativo de una dll en linux . usando JNI .. existe alguna manera de envolver la dll . para poder ser tratada como un .so o algo asi ?
Gracias de antemano ..


Replica a superpiwi Cancelar la respuesta