terça-feira, 16 de fevereiro de 2010

Tutorial simples de JNI no Linux c/ GCC

Introdução

O JNI (Java Native Interface) é uma interface que viabiliza interagir a JVM com bibliotecas nativas do SO subjacente. É utilizada principalmente para tarefas de mais baixo nível que a API Java não oferece ou, ainda, quando necessita-se de uma performance maior sem o "overhead" da JVM no caminho.

É importante ressaltar que a utilização de métodos nativos torna sua aplicação dependente de plataforma. Em Linux trabalharemos com arquivos de extensão SO (Shared Objects), já em Windows trabalha-se com DLLs (Dynamic Linked Libraries). O JNI, no entanto, não se limita a somente Linux e Windows, é possível utilizá-lo em outras plataformas (Solaris, AIX, etc) de acordo com a implementação JVM.

Esse tutorial tem o objetivo de mostrar um simples exemplo de utilização do JNI. Diga-se de passagem, a documentação da Sun sobre o assunto é muito rica e os links se encontram nas referências.

Primeiro passo: criar a classe que conterá o(s) método(s) nativo(s).

Crie um diretório "Carnival" (apenas por questão de organização), uma classe e um método com o modificador "native" sem implementação para trabalharmos no exemplo.

public class Carnival
{

// Carregamento da biblioteca que será criada.
static {
System.loadLibrary("Carnival");
}

// Método marcado com modificador "native".
public native void dance(String verse);

public static void main(String[] args) {
new Carnival().dance("...pode chorar, mas chora...");
}
}



Para que a JVM possa "linkar" a chamada do método da classe Carnival com uma biblioteca compartilhada nativa, ela precisará saber o nome. Aqui carregamos a biblioteca assim que a classe Carnival é carregada pela JVM:

static {
System.loadLibrary("Carnival");
}


Logo em seguida declaramos o método "dance()" que será chamado pelo método "main()". Como a implementação de um método "native" será escrita em outra linguagem (C ou C++), não iremos fornecer implementação e terminaremos a declaração com ponto-e-vírgula.

Segundo passo: compilar a classe.

javac Carnival.java


Ela será compilada normalmente, mesmo que as bibliotecas nativas ainda não estejam acessíveis. Você só não conseguirá executar ainda.

Terceiro passo: utilizar o "javah" para gerar os headers em C/C++.

javah -jni Carnival


* Certifique-se que a classe esteja em seu "classpath".

Isso gerará um arquivo Carnival.h que será usado ao criar a implementação do método.

Se quiser, examine o Carnival.h. Você encontrará os protótipos das funções que implementará em seguida.

Quarto passo: implementar o método nativo.

Crie um arquivo chamado Carnival.c e dentro dele virá a implementação do método "dance()".

#include <jni.h>
#include "Carnival.h"
#include <stdio.h>

JNIEXPORT void JNICALL
Java_Carnival_dance(JNIEnv * env, jobject obj, jstring verse)
{
const char * vstr = (*env)->GetStringUTFChars(env, verse, NULL);
printf("%s\n", vstr);
return;
}



Quinto passo: compilar a implementação.

Neste tutorial utilizaremos o GCC. No meu desktop utilizei um Makefile para poupar o trabalho, mas aqui vai o comando direto para compilar.

Antes criaremos um diretório "lib" que irá conter o arquivo "Shared Object" gerado pelo GCC:

mkdir lib

E então compilaremos:

gcc -o lib/libCarnival.so -shared -Wl,-soname,libCarnival.so \
-I/opt/jdk/include \
-I/opt/jdk/include/linux Carnival.c \
-static -lc

É importante lembrar que a biblioteca precisa estar no PATH de bibliotecas do Linux, então defina a variável de ambiente LD_LIBRARY_PATH para o diretório "lib" criado:

LD_LIBRARY_PATH="$(pwd)/lib"
export LD_LIBRARY_PATH

Com isso, ao executar a classe, a "JVM" conseguirá encontrar a biblioteca libCarnival.so.

Sexto (e último) passo: executar.

java Carnival

Espera-se, aqui, que seja impressa a mensagem que foi enviada através do parâmetro "verse" (String) pelo método "dance()" da classe Java Carnival.

Referências:

http://java.sun.com/docs/books/jni/html/start.html#26346

http://java.sun.com/developer/onlineTraining/Programming/JDCBook/jniexamp.html

http://www.ibm.com/developerworks/aix/library/au-JNI_AIX_PAPER.html

2 comentários:

  1. nao da, pois a bibplioteca jni.h o compilador nao encontra...

    ResponderExcluir
  2. Achei seu post excelente, me ajudou a entender muita coisa.
    Mas ainda não consegui resolver meu problema, tenho um método em C que carrega uma função de um arquivo “.o”, quando peço para compilar ele não encontra essa tal função… alguém consegue me ajudar?

    ResponderExcluir