Tutorial JNI
JNI, Java Native Interface, é uma suíte de programação que permite código em Java rodando sob uma máquina virtual Java (JVM) chamar e ser chamada por aplicações nativas. Aplicações nativas são programas escritos para plataformas de hardware e sistema operacional específicos. É possível fazer chamadas para C, C++ e Assembly.

JNI - Usando coisas que estão fora do Java
1. Porque?
- Aplicações legadas que não podem ser reimplementadas
- Bibliotecas nativas específicas que não possuem binding ou implementação em para Java
- Trechos críticos da aplicação onde a performance do Java é inferior a uma implementação nativa.
2. Como Fazer?
Vamos fazer um olá mundo do JNI, uma aplicação Java vai chamar um programa em C que imprime “Ola Mundo”.
Essa vai ser a classe OlaMundo:
class OlaMundo {
public native void diga ();
static {
System.loadLibrary("olanativo");
}
public static void main (String[] args) {
OlaMundo ola = new OlaMundo ();
ola.diga();
}
}
Primeiro nós declaramos uma classe chamada OlaMundo. Essa classe possui um método chamado diga que recebe nenhum parâmetro e não retorna nada. Através da palavra reservada native nos declaramos que esse método é uma implementação nativa e portanto sua implementação não feita aqui.
Em seguida nos declaramos um bloco estático e chamamos o método loadLibrary do System. Nós pedimos para ele abrir a biblioteca olanativo (isso será explicado melhor mais adiante). É importante que o nome da biblioteca esteja todo em minúsculo.
No método main nós instanciamos o objeto OlaMundo e chamamos seu método diga.
Compilamos isso:
$ javac OlaMundo.java
Se você escreveu tudo certo a compilação não irá dar nenhum problema.
Agora vamos tentar rodar:
$ java OlaMundo
Exception in thread “main” java.lang.UnsatisfiedLinkError: no olanativo in java.library.path
at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1682)
at java.lang.Runtime.loadLibrary0(Runtime.java:822)
at java.lang.System.loadLibrary(System.java:992)
at OlaMundo.(OlaMundo.java:5)
Não deu certo. Não foi possível linkar o programa com a biblioteca olanativo. Tudo bem, nós não escrevemos ela ainda. Isso não serviu pra nada, eu só mostrei isso para sublinhar que os nossos erros serão geralmente em tempo de execução e não em tempo de compilação.
Agora vamos gerar os cabeçalhos (headers) para fazermos nossa implementação nativa. A ferramenta javah faz isso pra nós, basta chamar javah seguido do nome da classe que vai chamar uma implementação nativa.
$ javah OlaMundo
Esse comando vai gerar esse OlaMundo.h:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include
/* Header for class OlaMundo */
#ifndef _Included_OlaMundo
#define _Included_OlaMundo
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: OlaMundo
* Method: diga
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_OlaMundo_diga
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
Não é importante você entender esse código acima. Nós vamos incluir ele com um include na nossa implementação. Ele inclui o jni.h onde estão declarados toda a parte de conversão de tipo do Java/C e vai declarar o cabeçalho da função diga, que agora passa a se chamar Java_OlaMundo_diga.
Agora vamos, finalmente, a nossa implementação nativa.
#include
#include "OlaMundo.h"
JNIEXPORT void JNICALL Java_OlaMundo_diga (JNIEnv * env, jobject jobj){
printf("ola mundo\n");
}
Nós incluímos o OlaMundo.h e escrevemos a função Java_OlaMundo_diga, que simplesmente imprime um “ola mundo” e pula a linha.
Agora vamos compilar esse código, mas primeiro temos que descobrir onde está o jni.h. Ele vai estar dentro da pasta onde seu java está instalado e isso vai variar muito de distribuição para distribuição. Alguns comandos podem te dar pistas:
$ whereis java
Isso vai te dar vários lugares, veja se em algum desses lugares tem uma pasta chamada include e lá dentro o jni.h. No Ubuntu que eu estou usando o java está em /opt/jdk1.6.0/include/.
Nos vamos compilar nosso código em uma biblioteca. Essa bibliteca vai se chamar libolanativo.so. Esse nome não foi escolhido arbitrariamente, ele é formado pela string que nós citamos no primeiro programa, na linha 5, precedida por “lib” e sucedida por “.so”. Se estivermos usando o Windows, seria libolanativo.dll.
É necessária uma longa linha de comando para compilar isso com o gcc, vamos a ela:
$ gcc -o libolanativo.so -shared -Wl,-soname,libolanativo.so -I/opt/jdk1.6.0/include -I/opt/jdk1.6.0/include/linux OlaMundo.c -static -lc
Lembrando mais uma vez que você vai trocar os dois /opt/jdk1.6.0/include pelo diretório do Java onde está o jni.h.
Está quase tudo pronto, mas se você tentar executar o OlaMundo agora vai dar aquele primeiro erro. A linkagem com a libnativo.so não poderá ser feita porque o java não consegue encontrar ela. Vamos setar uma variável de ambiente que diz onde se deve procurar as bibliotecas para serem linkadas, vamos dizer . (ponto) que quer dizer o diretório corrente.
No terminal:
$ LD_LIBRARY_PATH=.
$ export LD_LIBRARY_PATH
Rufem os tambores, já está tudo pronto para executar a classe OlaMundo. No terminal:
$ java OlaMundo
ola mundo
3. Desvantagens de se usar JNI
- Perda da portabilidade. Uma aplicação que usa uma implementação nativa só funcionará para os binários compilados disponíveis.
- Complicação. Muitos conceitos estão envolvidos para fazer uma aplicação JNI funcionar. É necessário conhecimento em mais de uma linguagem.
4. Links Úteis:
- Artigo da Wikipédia anglofôna sobre JNI
- Especificação da JNI
- Capítulo 5 do livro Advanced Programming for the Java 2 Platform
- Artigo da revista JavaWorld sobre JNI
- http://public.cabit.wpcarey.asu.edu/janjua/java/jni/
- http://www.codeproject.com/java/jnibasics1.asp
- http://java.sun.com/docs/books/tutorial/information/download.html
- http://www.gmonline.demon.co.uk/cscene/CS4/CS4-04.html
- http://www.javafree.org/javabb/viewtopic.jbb?t=13913

