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: