Java Native Interface

Surgiu a necessidade de usar o JNI (Java Native Interface) e tive muita dificuldade para encontrar informações sobre o assunto. Então resolvi compartilhar o que aprendi sobre este assunto.

[ Hits: 22.171 ]

Por: Allan kardec Santos Oliveira em 22/09/2011


Um exemplo mais complexo



Como havia dito anteriormente, o que me motivou a utilizar o JNI foi criar uma forma de utilizar funções da biblioteca libparted.so no Java. Este exemplo vai mostrar a solução encontrada para esta situação.

Vamos criar o arquivo jparted.java com uma função getParticoes que receberá uma string contendo o dispositivo(hd) e devolverá um array de string contento informação das partições encontradas no hd.

Para este exemplo é necessário ter o parted instalado em sua distribuição(ftp://ftp.gnu.org/gnu/parted/parted-3.0.tar.gz).

A ideia é implementar uma função nativa escrita em C++ que por sua vez vai lê as partições do hd usando as funções que estão implementadas na biblioteca libparted.so.

Vamos para o código:

jparted.java:

public class jparted{ public native String[] get_partition(String device); static { System.loadLibrary("jparted"); //carrega a biblioteca libjparted.so } public static void main(String args[]){ jparted obj = new jparted(); //substitua /dev/sda pelo dispositivo do seu hd //certifique que tenha permissão para ler o arquivo /dev/sda String retorno[] = obj.get_partition("/dev/sda"); for (int i = 0; i < retorno.length; i++) System.out.println(retorno[i]); } }

Compilando...

javac jparted.java

Gerando o arquivo de cabeçalho em C/C++:

javah -jni jparted

Arquivo jparted.h gerado pelo javah:

jparted.h:

/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class jparted */ #ifndef _Included_jparted #define _Included_jparted #ifdef __cplusplus extern "C" { #endif /* * Class: jparted * Method: get_partition * Signature: (Ljava/lang/String;)[Ljava/lang/String; */ JNIEXPORT jobjectArray JNICALL Java_jparted_get_1partition (JNIEnv *, jobject, jstring); #ifdef __cplusplus } #endif #endif

Vamos criar a implementação do arquivo da função java_jparted_get_1partition no arquivo jparted.cpp:

#include "jparted.h" #include <parted/parted.h> #include <iostream> #include <sstream> using std::string; using std::ostringstream; string intToStr(int numero); //função para auxiliar a conversão de dados. char *StringToChar(string str); //função para auxiliar a conversão de dados. JNIEXPORT jobjectArray JNICALL Java_jparted_get_1partition (JNIEnv *env, jobject obj, jstring device){ //transfererindo o conteudo do jstring para char* const char* cdevice = env->GetStringUTFChars(device,NULL); string str; //cria um string para facilitar a manipulação //acessando o disco definido em cdevice /* os tipos PedDevice, PedDisk, PedPartition e as funções ped_device_get, ped_disk_new, ed_disk_next_partition, ped_destroy_disk, ped_destroy_device se encontram no arquivo libparted.so definidas em parted.h */ PedDevice* device_hd; PedDisk* disk_hd; PedPartition *particao = NULL; device_hd = ped_device_get(cdevice); //carrega o device(/dev/sda) no ponteiro device_hd disk_hd = ped_disk_new(device_hd); //carrega o ponteiro disk_hd char *nome_part; //variável para receber o nome da partição char *cRetorno[30]; //retorna ate 30 particoes int i=0; while ((particao = ped_disk_next_partition(disk_hd, particao))) //carrega a partição em *particao { if (particao->num < 1 || ! particao->fs_type) //se a partição for invalida ou estendidas continua o loop continue; nome_part = (char*)particao->fs_type->name; //obtem o nome da str.assign(nome_part); //carrega o conteudo do ponteiro nome_part em str(string) str = cdevice + intToStr(particao->num) +" "+str; //concatena cdevice com o número da particao cRetorno[i] = StringToChar(str); i++; } ped_disk_destroy(disk_hd); ped_device_destroy(device_hd); //criando retorno para o java jobjectArray retorno; //cria um array jobjecArray com i elementos. retorno = (jobjectArray)env->NewObjectArray(i,env->FindClass("java/lang/String"),env->NewStringUTF("")); //adiciona o conteudo de cRetorno em retorno. for (int x = 0; x < i; x++){ env->SetObjectArrayElement(retorno,x,env->NewStringUTF(cRetorno[x])); } return retorno; } string intToStr(int numero){ std::ostringstream svalor; svalor << numero; return svalor.str(); } char *StringToChar(string str){ char *retorno = new char[str.length()+1]; for (unsigned int c = 0; c < str.length(); c++) retorno[c] = str[c]; retorno[str.length()] = 0; //inserindo o valor null no final da string return retorno; }

Para compilar este arquivo é necessário inserir nos argumentos de compilação a opção -lparted. Esta opção indica ao compilador que a biblioteca libparted.so deverá ser compilada junto com o arquivo jparted.cpp.

g++ -fPIC -shared -o libjparted.so jparted.cpp -lparted

Copie o arquivo libjparted.so para a pasta /usr/lib64 ou /usr/lib dependendo do tipo de sistema operacional 32/64bits.

Para testar sua aplicação java, certifique que tenha informado o dispositivo correto no arquivo jparted.java. No meu caso usei /dev/sda. Certifique também que tem acesso a leitura deste dispositivo.

# chmod +r /dev/<dispositivo>

Vamos testar o arquivo jparted.class:

java jparted

Se tudo estiver correto você deve obter um resultado semelhante a este:

Warning: Unable to open /dev/sda read-write (Permission denied). /dev/sda has been opened read-only.
Warning: Unable to open /dev/sda read-write (Permission denied). /dev/sda has been opened read-only.
/dev/sda1 ntfs
/dev/sda5 fat32
/dev/sda6 reiserfs
/dev/sda7 reiserfs
/dev/sda8 reiserfs
/dev/sda9 reiserfs
/dev/sda10 linux-swap(v1)


Conclusão

O recurso JNI é muito poderoso e pode ser usado quando a linguagem java tiver alguma limitação para executar alguma tarefa. Este artigo mostra somente uma pequena porção do que o JNI é capaz de fazer.

Espero que este artigo ajude a comunidade caso necessitem usar o JNI ou pelo menos direcione para encontrar mais informações.

Para verificar todo o poder deste recurso, recomendo que leiam:
Página anterior    

Páginas do artigo
   1. O que é o JNI
   2. Funcionamento
   3. Exemplo de utilização do JNI
   4. Um exemplo mais complexo
Outros artigos deste autor

Criando classe Java para conectar e manipular dados no MySQL

Emulando o Internet Explorer no Slackware

Construindo uma aplicação JDialog

Leitura recomendada

Jmeter com qualidade e performance

Configurando e-Gen + Tomcat + JSDK

GCJ – Conhecendo o compilador Java Livre

Testes unitários em Java com JUnit

Funções Completas - Comunicação entre aplicações Android e FTP

  
Comentários
[1] Comentário enviado por asdf2 em 23/09/2011 - 00:42h

artigo bom mas esqueceu de por um simples hello world pra comparação de codigo, mas valeu, nota 8

[2] Comentário enviado por elyssonmr em 23/09/2011 - 10:31h

Muito legal isso, só não entendi uma coisa: você utiliza o Header da lib parted e depois gera o .so compatível com o jni certo?

Se já possuir um .so pronto como eu faria para linkar com o java, porque neste caso o a lib a ser utilizada esta sendo compilada?

Vlw

[3] Comentário enviado por super-root em 23/09/2011 - 17:58h

Elyssonmr, caso você tenha uma lib no padrão do JNI, então será necessário conhecer as funções/declarações desta biblioteca. Desta forma seria necessário criar somente o programa em java com lendo as funções da biblioteca.


Contribuir com comentário




Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts