4
/ 38 jna_ Descubra como é simples integrar suas aplicações Java com bibliotecas nativas do Sistema Operacional H istoricamente, 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 ambiente A ú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

Simplificando o acesso a código nativo com JNA · tar essa tarefa ao permitir fácil acesso às bibliotecas ... que pode ser baixado diretamente do Github do pro-jeto ... para download

  • Upload
    trannga

  • View
    213

  • Download
    0

Embed Size (px)

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 | [email protected]é 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 | [email protected]é 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