quarta-feira, 7 de agosto de 2013

JNA (Java Native Access)



JNA é um framework permite acessos a bibliotecas nativas (DLL em Windows e so em Linux).

 Integrando com Shared Object (Linux)

 Vamos criar um arquivo simples em C que realiza uma soma ou uma subtração de acordo com um parâmetro recebido. O parâmetro recebido é propositalmente do tipo char* para demonstrar a compatibilidade com o tipo String existente no Java, assim como os outros parâmetros.

Segue portanto o código do arquivo calcjna.c :

#include "stdio.h"
#include "strings.h"
#include "calcjna.h"

int calcular(char* op, int arg1, int arg2) {
           int calc = -1;
if(strcmp(op,"soma") == 0) {
calc =  arg1 + arg2;}
else
if(strcmp(op,"subtracao") == 0) {
calc = arg1 - arg2;}
else {
printf("Operacao %s invalida\n", op);
return calc;
}
printf("Operacao de %s. Executada com sucesso!\n", op);
return calc;
} 

Não podemos esquecer de criar o arquivo Header. Segue o código do arquivo calcjna.h : 

int calcular(char* op, int arg1, int arg2); 

Agora para gerarmos a biblioteca compartilhada:

gcc calcjna.c -shared -I. -o libcalcjna.so 

Pronto, criamos nossa biblioteca nativa (Certifique-se de que o nome do objeto criado inicia com lib). Agora vem a parte interessante que é a comunicação da linguagem Java com o código nativo via JNA. Para fazer isso devemos criar uma interface em Java que fará o mapeamento dos tipos. O mapeamento de tipos compatíveis pode ser encontrado aqui sob a sigla "Default Type Mappings".  

Segue o código de nossa interface ICalcJNA.java : 

import com.sun.jna.Library;
public interface ICalcJNA extends Library {
public int calcular(String operacao, int elemento1, int elemento2);
} 

Veja que mapeamos o tipo char* para o tipo String do Java, bem como o tipo int que é o mesmo em ambas linguagens.  

Estamos quase no fim, só falta criar o arquivo responsável pela carga da biblioteca, que terá o nome de CalcJNA.java : 

import com.sun.jna.Native;

public class CalcJNA {
public static void main(String args[]) {
ICalcJNA calc = 
(ICalcJNA) Native.loadLibrary("calcjna", ICalcJNA.class);
int i = calc.calcular("soma", 10, 5);
System.out.println(i); i = calc.calcular("subtracao", 10, 5); System.out.println(i); i = calc.calcular("divisao", 10, 5); System.out.println(i);
}
}

OBS Linux

Devemos  informar somente o nome da biblioteca sem o inicio lib e sem o final .so.

A partir de agora é só compilar e executar o código, caso você não tenha gravado o arquivo jna.jar em algum diretório que o Java o encontre, deveremos informar explicitamente ao compilador onde ele deve buscar as classes que utilizamos, no meu caso o arquivo JAR está no mesmo diretório que estão todos os outros arquivos que criamos aqui. Portanto, vamos compilar:

 javac -cp .:./jna.jar CalcJNA.java

 Após isto é so rodar o programa:

java -cp .:./jna.jar CalcJNA

O resultado deveria ser esse:

Operacao de soma. Executada com sucesso!
15

Operacao de subtracao. Executada com sucesso!
5

Operacao divisao invalida
-1
 

Integrando com Windows (DLL)
 

O objetivo aqui será obter a data e a hora do sistema através de uma biblioteca nativa do sistema operacional, vamos obter essas informações através da DLL KERNEL32.DLL que fica localizada no diretório de instalação do Windows e na subpasta SYSTEM32.
 
1 Passo: Criar nossa interface que é a única parte burocrática na utilização do JNA:

import com.sun.jna.win32.StdCallLibrary;
public interface IKernel32 extends StdCallLibrary {
   void GetSystemTime(DataHoraSistema result);
} 

Podemos notar que existe uma diferença aqui nessa interface, aqui ela não é herança de Library, mas sim de StdCallLibrary. Isso porque essa DLL usa a convenção __stdcall de chamada de métodos, porém na maioria dos casos as bibliotecas serão herança de Library mesmo.

Definimos no código acima a interface de acesso ao método GetSystemTime que recebe um parâmetro do tipo Structure pois o método nativo recebe como parâmetro uma estrutura nativa e que será populada com informações de data e hora do sistema.

Para isso teremos que definir uma classe Java que será herança da classe Structure. Isso só é necessário quando métodos nativos recebem ou devolvem parâmetros do tipo de estruturas nativas. Lembrando que a DLL em questão possui vários outros métodos, mas como utilizaremos apenas um, definimos apenas ele. Salve esse arquivo como IKernel32.java.


2 Passo: Vamos então mapear nossa estrutura Java para trocarmos informações com a biblioteca nativa através dessa estrutura:

 import com.sun.jna.Structure;

 public class DataHoraSistema extends Structure {
   public short wYear;
   public short wMonth;
   public short wDayOfWeek;
   public short wDay;
   public short wHour;
   public short wMinute;
   public short wSecond;
   public short wMilliseconds;
}

Criamos aqui um tipo que é herança de Structure e mapeamos os tipos para short que é o equivalente ao tipo WORD do Windows. Para isso utilizamos a tabela de mapeamento que encontramos aqui como citado no primeiro artigo. Salve como DataHoraSistema.java.

3 Passo: Implemente a interface IKernel32:
 

import com.sun.jna.Native; 

public class Kernel32JNA {
   public static void main(String args[]) {
      IKernel32 lib =
         (IKernel32) Native.loadLibrary("kernel32", IKernel32.class);
      DataHoraSistema time = new DataHoraSistema();
      lib.GetSystemTime(time);
      System.out.println("Data: " + time.wDay +
                         "/" + time.wMonth +
                         "/" + time.wYear);
      System.out.println("Hora: " +
                         time.wHour +
                         ":" +
                         time.wMinute);
   }
}

No código acima apenas obtemos uma instância da biblioteca KERNEL32.DLL através do método loadLibrary do JNA que é quem faz toda a mágica e, a partir daí, trabalhamos com os tipos e métodos fornecidos pela própria biblioteca nativa e que mapeamos na classe DataHoraSistema do Java. 

A partir daí é só a gente compilar:

javac -cp .;jna.jar Kernel32JNA.java

E executar:

java -cp .;jna.jar Kernel32JNA

Resultado:

Data: 5/2/2008
Hora: 13:26

Nenhum comentário:

Postar um comentário