View
213
Download
0
Category
Preview:
Citation preview
/ 38
jna_
Descubra como é simples integrar suas aplicações Java com bibliotecas nativas do Sistema Operacional
Historicamente, acessar métodos de uma bibliote-ca nativa do sistema operacional com Java sem-
pre foi uma tarefa tediosa, afinal era necessário a escrita de muito ‘glue code’ para alcançar tal integra-ção. A JNA (Java Native Interface) se propõe a facili-tar essa tarefa ao permitir fácil acesso às bibliotecas nativas usando apenas código Java para realizar essa integração.
A ideia deste artigo surgiu quando, recentemen-te, tivemos a necessidade de realizar a integração de um aplicativo Java desenvolvido em Swing com uma DLL proprietária para emissão de cupom fiscal. No entanto, utilizando a abordagem mostrada nes-te artigo o leitor conseguirá realizar essa integração de forma simples e eficiente em todos os contextos onde essa necessidade se faça presente.
Nossos exemplos serão baseados na integração da Linguagem C com a plataforma Java. Os códigos escritos em C serão compilados para bibliotecas na-tivas e mostraremos como um aplicativo desenvolvi-do em Java poderá enviar e consumir informações da biblioteca nativa usando a JNA como ‘ponte’ entre as duas linguagens.
Configuração do ambienteA única dependência necessária para a execução
dos exemplos mostrados neste artigo é o jar da JNA, que pode ser baixado diretamente do Github do pro-jeto (https://github.com/twall/jna). Efetue o down-load do arquivo jna.jar e insira-o no seu classpath.
Conhecendo a JNA através de um exemplo básico
Começaremos nosso estudo em JNA executando um exemplo simples, onde acessaremos uma DLL nativa do Windows para bloquear a estação de tra-balho (atalho Logo+L).
A interação com uma biblioteca nativa requer a criação de uma interface que servirá de ‘fachada’ para o acesso à biblioteca, contendo os métodos que serão acessados pela nossa aplicação. A figura 1 de-monstra a interação entre os componentes do nosso exemplo.
Após o entendimento do fluxo, crie a interface de fachada conforme a listagem 1. Essa interface deverá estender a interface com.sun.jna.library e irá conter a declaração de todos os métodos que nos-sa aplicação irá acessar da biblioteca nativa.
Listagem 1. Interface de fachada para comunicação com a biblioteca nativa.
import com.sun.jna.Library;public interface User32 extends Library { boolean lockWorkStation();}
Após a criação da interface, podemos implemen-tar a classe de acesso à biblioteca nativa, conforme a Listagem 2.
Simplificando o acesso a código nativo
com JNA
39 \
Sabemos que a Linguagem Java foi projetada desde os seus primór-dios para ser independente de sistema operacional. No entanto, em algum momento pode ser necessário integrar suas aplicações com as bibliotecas nativas do sistema operacional. Neste artigo, vere-mos como alcançar essa integração de forma simples usando a JNA (Java Native Interface).
Rafael Roque viana | rafael.roque@stds.ce.gov.bré graduando em Análise e Desenvolvimento de Sistemas pela FIC e atua como Analista de Sistemas na STDS (Secretaria do Tra-
balho e Desenvolvimento Social do Estado do Ceará) com foco no desenvolvimento de aplicações Java para Web. Possui 7 anos de experiência na plataforma Java e é certificado SCJP, SCWCD, SCEA(I) e ITIL V2.
João Paulo de Oliveira Franco | joaopaulo.franco@stds.ce.gov.bré graduando em Análise e Desenvolvimento de Sistemas pela FLF e atua como Programador Java na STDS (Secretaria do Trabalho
e Desenvolvimento Social do Estado do Ceará) com foco no desenvolvimento de aplicações Java para Web.
Listagem 2. Classe de acesso aos métodos nativos.
import com.sun.jna.Native;public class LockWorkStation{ public static void main(String[]args){ User32 user32 = (User32) Native.loadLibrary (“user32”,User32.class); user32.lockWorkStation(); }}
A String passada como argumento para o método loadLibrary(String,Class) representa o nome da biblioteca nativa a ser acessada pelo proxy. A figura 2 apresenta o padrão de nomenclatura de bibliote-cas nas plataformas mais conhecidas. Esse método lança uma java.lang.UnsatisfiedLinkError caso a biblioteca não seja encontrada.
Figura 2. Padrão de nomenclatura de bibliotecas de acordo com o Sistema Operacional.
Parâmetros de entrada e valores de retorno
Nosso próximo exemplo mostra-se a chamada de um método recebendo parâmetros de entrada e retornando um valor do tipo int. O método recebe dois parâmetros do tipo int e retorna a soma desses valores, conforme a listagem 3.
Figura 1. Fluxo de interação.
Código da Aplicação
com.sun.jnaNative
DLL Proxy
user32.dll
loadLibrary ()
LockWorkStation ()LockWorkStation ()
<<create>>
Java
JNA
Código Nativo
/ 40
Criando uma biblioteca nativa O processo de criação de uma biblioteca nativa é
semelhante à compilação de um arquivo .java em um executável .class. A única diferença é que, enquanto o bytecode Java pode ser executado em diferentes plataformas, a biblioteca nativa está vinculada ao sistema operacional no qual foi compilada.
Para compilar o código-fonte escrito na lingua-gem C numa dll para Windows, foi usada a ferramen-ta MingW (Minimalist GNU for Windows), disponível para download em sourceforge.net/projects/mingw/files/MingW.
Após efetuar a instalação da ferramenta, cole o código mostrado na Listagem 3 no seu editor de textos preferido e salve com o nome “soma.c”. Feito isso, use a linha de comando para navegar até o di-retório onde o arquivo foi salvo e execute os coman-dos mostrados na Listagem 4 para criar a biblioteca nativa.
O comando “gcc -c” compila o código C e gera um executável, enquanto o comando ‘gcc -shared’ efetua a criação da biblioteca propriamente dita.
Listagem 3. Método com recebimento de argumen-tos e retorno de valor.
int soma (int x, int y){ return x + y;}
Listagem 4. Comandos para criação da dll.
gcc -c soma.cgcc -shared -o soma.dll soma.o
O projeto completo, que poderá ser baixado di-retamente do site da revista, está estruturado con-forme mostrado na figura 3. Semelhante ao primeiro exemplo, precisamos criar a interface para acesso a biblioteca (Listagem 5) e uma classe para testarmos a execução (Listagem 6). A classe BibliotecaUtil mos-trada na Listagem 7 foi criada para facilitar o carre-gamento das bibliotecas nativas.
Figura 3. Estrutura do Projeto.
Listagem 5. Interface de fachada.
import com.sun.jna.Library;public interface Soma extends Library { int soma(int x,int y);}
Listagem 6. Classe para execução do teste.
import util.BibliotecaUtil;
public class Calculadora { public static void main(String[] args) throws ClassNotFoundException { Soma soma = (Soma) BibliotecaUtil. carregaBiblioteca(“Soma”, “soma.dll”); System.out.println(soma.soma(40, 55)); }}
Listagem 7. Classe utilitária para carregamento das bibliotecas.
package util;
import com.sun.jna.Native;
public class BibliotecaUtil { public static Object carregaBiblioteca(String classe, String nomeArquivo) throws ClassNotFoundException{ String raizApp = System.getProperty(“user.dir”); String separador = System.getProperty( “file.separator”); String diretorioLib = “native”;
String caminho = raizApp +separador+ diretorioLib+separador+nomeArquivo; System.load(caminho); Class clazz = Class.forName(classe); return Native.loadLibrary(clazz); }}
Embora uma biblioteca nativa possa conter uma quantidade potencialmente grande de métodos, na interface só é necessário declarar os méto-dos que serão efetivamente acessados pela sua aplicação.
41 \
Trabalhando com tipos complexosAvançando nosso exemplo, podemos fazer o ma-
peamento de tipos complexos entre a linguagem C e Java. Em C, o conceito de classe é explicitado através de structs, que nada mais são do que um conjunto de atributos correlacionados. O exemplo abaixo de-monstra essa integração através da criação de uma Struct em C e a classe correspondente em Java para ler um objeto instanciado pela biblioteca nativa.
Observe na Listagem 9 a criação da classe estáti-ca ‘Pessoa’. Essa classe serve para mapear a estrutura da struct mostrada na Listagem 8 e irá receber o ob-jeto de retorno que será buscado da struct. Não ne-cessariamente essa classe deve ser interna à interface que representa a biblioteca, no entanto optamos por essa abordagem por questões de simplicidade. A clas-se que mapeia a struct deve estender a classe com.sun.jna.Structure.
Note a criação da classe ByValue’ na classe Pes-soa. O que ocorre é que, por padrão, uma classe Java que estenda de Struct é interpretada como ponteiro pelo lado nativo quando passada como parâmetro. Para ‘forçar’ a passagem por valor, é necessário a cria-ção de uma classe que estenda de structure.byva-lue. Note que essa classe também é usada para pegar o retorno da função nativa.
A listagem 10 ilustra a instanciação de um obje-to do tipo Pessoa, cujos atributos serão recuperados da biblioteca nativa.
Listagem 8. Tipo complexo representado através de uma struct.
typedef struct{const char *nome;int idade;} Pessoa;Pessoa retorno(){ Pessoa p ={“rafael”,29}; return p;}
Listagem 9. Mapeamento do tipo complexo em Java para uma struct.
import com.sun.jna.Library;import com.sun.jna.Structure;
public interface IClasse extends Library { public static class Pessoa extends Structure{ public static class ByValue extends Pessoa implements Structure.ByValue{} public String nome;
public int idade; } Pessoa.ByValue retorno();}
Listagem 10. Classe principal para execução do exemplo.
import util.BibliotecaUtil;
public class TesteClasse { public static void main(String[] args) throws ClassNotFoundException { IClasse iclasse = (IClasse)BibliotecaUtil. carregaBiblioteca(“IClasse”, “classe.dll”); IClasse.Pessoa p = iclasse.retorno(); System.out.println(“Nome:”+p.nome); System.out.println(“Idade:”+p.idade); }}
Considerações FinaisApesar de não ser uma necessidade recorrente na
maioria dos projetos, conhecer a integração Java com código nativo é um item importante no ‘cinto de fer-ramentas’ de qualquer desenvolvedor. Afinal, nunca se sabe quando será necessário reutilizar componen-tes de terceiros para resolver nossas tarefas do dia--a-dia.
Este artigo se propôs a ser uma introdução à in-tegração da linguagem Java com código nativo. Es-peramos que os exemplos aqui mostrados tenham despertado o interesse do leitor nessa ainda pouco conhecida API da Linguagem Java.
> Github do projeto: https://github.com/twall/jna
> Documentação Oficial da JNA: http://jna.java.net/
javadoc/overview-summary.html
> Artigo:http://today.java.net/article/2009/11/11/simplify-
native-code-access-jna
/referências
Recommended