SemtexGirl

É uma garota dinâmica e de bom gosto, uma garota que explode, que não para e nem aceita um não como resposta.

Minha primeira tentativa de artigo técnico

Há um bom tempo atrás comecei a escrever um post sobre JNI que acabou virando um esboço de um artigo que não saiu do 'papel', deixei ele perdido no meu computador e só agora quase 4 meses depois resolvi colocá-lo aqui mesmo no blog, o artigo é enorme então vou postar um pedacinho a cada dia, assim tenho tempo para revisá-lo e de bem, se alguem mais ler posso saber se consegui me fazer entender.

JNI - Rompendo as barreiras do acesso às aplicações nativas I

Há pouco tempo precisei desenvolver uma aplicação Java integrada com funções nativas e me vi perdida em meio de uma série de complicações na hora de utilizar JNI. Infelizmente, na época, não encontrei nenhuma documentação que se aprofundasse no assunto. Principalmente quando este era envio/manipulação de estrutura/objetos, conversei com outros desenvolvedores na empresa em que trabalhava e todos me aconselharam a fazer algum ‘ajuste técnico’ no lugar de trabalhar com o envio de objetos. Apesar de esta sugestão ser a saída mais fácil, não me pareceu nada elegante retornar um string concatenando todos os dados da estrutura, ou criar métodos auxiliares que me retornariam cada atributo da estrutura em C separadamente.
Depois de quebrar a cabeça para entender melhor o funcionamento do JNI finalmente consegui trabalhar com objetos Java dentro do C, e é isso que vou tentar explicar abaixo. Mas antes foi explicar um pouco sobre o que é e como funciona o JNI sem no entendo me estender muito, pois estas explicações se acham facilmente digitando JNI no Google, vou tentar explicar como enviar e trabalhar com os objetos Java em C.

Introdução

Em Java para nos comunicarmos com bibliotecas nativas utilizamos o JNI uma interface de comunicação desenvolvida pela Sun que permite de forma bidirecional que os códigos executados pela Java Virtual Machine (JVM) acessem aplicações e bibliotecas desenvolvidas em outras linguagens, como C, C++ e Delphi como se fossem métodos Java, facilitando, por exemplo, a integração de sistemas legados.

JNI 1

Figura1 : Representação das camadas de comunicação

Porem existe algumas premissas para a utilização de JNI são elas:

1. Implementação de funcionalidades que as API's Java não fornecem
2. Problemas de desempenho.
ex.
Um algoritmo que em Java fique muito lento pode ser implementado em C ou Assembly e acessado pelo Java.
3. Ou ainda, quando o código nativo deseja ter acesso à API Java.

O desenvolvimento de um programa Java que chama funções nativas pode ser dividido em seis passos:

1. O primeiro passo é escrever o código Java que execute três tarefas;

Declaração do método nativo que estaremos chamando;
Carga da biblioteca compartilhada que contém o código nativo;
E que execute uma chamada ao método nativo.

Como exemplo, criaremos uma aplicação Java que execute um método nativo que imprima a mensagem “Olá Mundo!”.

Listagem 1 : OlaMundo.java

Como podem notar na listagem 1, os métodos nativos carregam o modificador native, e sua função nativa correspondente será implementada na forma de uma biblioteca dinâmica conforme veremos em breve.

Notamos ainda a existência do método:

void <system>.loadLibrary(String libraryName)
 

Observe que ele recebe como parâmetro o nome da biblioteca dinâmica que iremos utilizar. A biblioteca deve ser carregada antes da chamada de qualquer método nativo.

2. Compilar o código Java;

javac OlaMundo.java

3. Criar o arquivo de header C/C++. O arquivo de header C/C++ declarará a assinatura da função nativa que queremos chamar. Este header será usado durante a implementação da função C/C++ (veja Passo 4) onde criaremos a nossa biblioteca dinâmica.

javah OlaMundo

Lembre-se que OlaMundo é o nome da sua classe Java já compilada a partir do qual geraremos o arquivo OlaMundo.h, como podem ver na listagem 2.

  1. /* DO NOT EDIT THIS FILE - it is machine generated */
  2. #include <jni.h>
  3. /* Header for class OlaMundo */
  4.  
  5. #ifndef _Included_OlaMundo
  6. #define _Included_OlaMundo
  7. #ifdef __cplusplus
  8. extern "C" {
  9. #endif
  10. /*
  11.  * Class: OlaMundo
  12.  * Method: saudacoes
  13.  * Signature: ()V
  14.  */
  15. JNIEXPORT void JNICALL Java_OlaMundo_saudacoes
  16. (JNIEnv *, jobject);
  17.  
  18. #ifdef __cplusplus
  19. }
  20. #endif
  21. #endif
  22.  

Listagem 2: OlaMundo.h

4. Escrever o código nativo. Este passo consiste em implementar a função desejada em C/C++, notem que no código da listagem 3 incluímos o arquivo de header OlaMundo.h criado no passo anterior.

  1. /*OlaMundo.c*/
  2.  
  3. #include <jni.h>
  4. #include
  5. <olaMundo.h>
  6.  
  7. JNIEXPORT void JNICALL Java_OlaMundo_saudacoes
  8. (JNIEnv *, jobject){
  9. printf("Olá Mundo!\n");
  10. return;
  11. }
  12.  

Listagem 3 : OlaMundo.c

5. Criar o arquivo de biblioteca dinâmica.

Agora que todo o código necessário foi escrito, podemos compilar a biblioteca nativa. Ela será invocada dinamicamente quando a classe OlaMundo tiver o método saudacoes() invocado. Para efetuar a compilação devemos considerar o sistema operacional e o compilador utililizado.

Se estivermos utilizando o Windows e o Microsoft Visual Studio, utilizaremos o comando abaixo:

cl -Ic:\jdk1.5.0_07\include -Ic:\jdk1.5.0_07\include\win32 -MD -LD OlaMundo.c OlaMundo.dll

Já em Solaris poderíamos utilizar:

cc -G -I/java/include -I/java/include/solaris OlaMundo.c -o libOlaMundo.so

E em Linux o comando correspondente seria:

gcc -o libOlaMundo.so -shared -Wl,-soname, libOlaMundo.so -I/export/home/java/include -I/export/home/java/include/linux OlaMundo.c -static –lc

Concluído o passo 5 teremos uma aplicação JNI pronta esperando para ser executada.

6. Executar o programa.

Neste ponto, têm-se dois componentes prontos para serem executados. O arquivo “.class” (OlaMundo.class) chama o método nativo e a biblioteca nativa (olamundo.dll ou libOlaMundo.so) implementa o método nativo. Como a classe OlaMundo contém sua própria função main, ela pode ser executada da seguinte maneira:

> java OlaMundo

Durante a execução do programa podemos obter algumas mensagens de erro, sendo que as três mais comuns são:

1. Uma ligação dinâmica (dynamic link) não pode ser encontrada. Isto resulta na mensagem de erro: java.lang.UnsatisfiedLinkError. Normalmente, esta mensagem significa que ou o arquivo de biblioteca dinâmica, ou um método nativo específico dentro da biblioteca dinâmica não pode ser encontrado.

2. O arquivo de biblioteca dinâmica não pode ser encontrado. Isso acontece quando a JVM não consegue localizar o arquivo de biblioteca indicado no loadLibrary(), caso isto ocorra devemos verificar se o nome do arquivo esta escrito corretamente e que não se tenha especificado a extensão. Devemos, também, verificar se o arquivo de biblioteca encontra-se acessível à JVM. Caso desejemos utilizar outros caminhos, que não os padrões, existem duas técnicas. A primeira consiste em setar a variável de ambiente PATH, no Windows, ou LD_LIBRARY_PATH, para Linux e Solaris, com o caminho desejado. E a outra é setar este caminho através de um parâmetro, durante a chamada do programa java:

java -Djava.library.path=. OlaMundo

Ola mundo!

No caso o parâmetro –D informa à máquina virtual que existe uma variável de ambiente do Java chamada java.library.path e ela será analisada pela máquina virtual como outro caminho alternativo aonde ir buscar as bibliotecas dinâmicas e até mesmo outras classes utilizadas no projeto.

No exemplo acima estamos informando que o arquivo de biblioteca dinâmica encontra-se no diretório atual.

3. Um método com a assinatura especificada não pode ser encontrado. A implementação da função C/C++ deve possuir uma assinatura idêntica à assinatura da função no arquivo de header. Se quiser saber um pouco mais sobre assinatura/descritor de argumento é só ler o próximo capítulo e este não for o seu caso vá direto para o capitulo “Acessando os atributos de um objeto”.

Agora que já relembramos os passos para a criação de uma aplicação JNI, podemos nos aprofundar um pouco mais sobre seu funcionamento.

O próximo post será sobre assinatura ou descritores de argumento.

6 comments

6 Comments so far

  1. A. janeiro 19th, 2007 16:06

    Se eu entendesse o que está escrito, deixava um comentário.

    Mas… deve ser bom. Ou estar bom. Ah, vc entendeu.
    :-P

  2. Frank abril 17th, 2007 7:04

    Boa sua análise.
    O resto do artigo? jejeje
    uma pergunta:
    quando for desenvolver o codigo nativo tem que estar adequando a sintaxe?
    ex.:
    //código em C puro
    #include
    #include

    void saudacoes{
    printf(“Olá mundo!\n”);
    return;
    }

    //Código adequado para que a biblioteca possa ser //chamada via JNI.
    #include
    #include

    JNIEXPORT void JNICALL Java_OlaMundo_saudacoes
    (JNIEnv *, jobject){
    printf(“Olá Mundo!\n”);
    return;
    }

  3. Andréia Bustamante abril 30th, 2007 21:22

    Ola Frank,

    Desculpe, pela demora, eu ando um pouco afastada do meu blog ;/
    Mas a estrutura é esta mesmo. No inicio a gente estranha… mas depois você vai ver que fica tudo tranquilo.

  4. Filipe maio 21st, 2007 13:08

    Olá..Estou incluido num projecto que necessito de efectuar chamadas a código nativo..Consegui seguir todo o seu artigo excepto na parte de criação da .dll! Eu estou em windows e utilizei MVS e ao correr o comando fornecido retorna-me este erro “too many input files”.Tem alguma idea do que posso estar a fazer de errado?
    Cumprimentos.

  5. Andréia Bustamante junho 30th, 2007 23:12

    Putz! Foi mal mas o e-mail avisando do post caiu no SPAM list do gmail e eu não tenho visitado nem escrito nada nos últimos tempos… foi mal…. e a esta hora você já resolveu o seu problema… mas se precisar de alguma coisa me manda um e-mail…

    andreia [dot] bustamante [at] gmail [dot] com

  6. Tiago janeiro 29th, 2009 18:29

    Olá Andréia,

    Estou desenvolvendo um acesso a hardware com JNI pelo Linux através de um applet java. O erro que me retorna é sempre que a biblioteca dinâmica não pode ser encontrada na java.library.path. Onde eu devo colocar minha biblioteca para que a JVM possa encontrá-la?

    Obrigado por sua atenção.

    Abraço