terça-feira, 1 de dezembro de 2009

Validador de HASH MD5 visual

Todas as vezes que eu baixo um arquivo e preciso validar um hash md5 eu tenho que abrir uma linha de comando e digitar:
md5sum /caminho/do/arquivo/nome_do_arquivo.extensao

Seria legal se eu clicasse com o botão direito sobre um arquivo e ele me exibisse o HASH já não?

Como eu uso Ubuntu, comecei garimpar na web em busca de um script para o Nautilus e infelizmente não encontrei. Deve exisitir? Claro que sim, mas como eu não achei assim de "sopetão" achei que seria mais fácil eu criar o meu próprio. Criei um scriptzinho aqui em 5 minutos e comecei a usar. Daí uns amigos pediram e os amigos dos amigos pediram então pensei em disponibilizar isso no Sourceforge.

Pronto. O projeto está aqui é só baixar e usar. Lembrando que está muito, muito, muito simples pois fiz para uso próprio. Se alguém quiser melhorá-lo seja com código, seja relatando bugs, eu prometo fazer o possível pra atualizar lá apesar de estar bem sem tempo ultimamente! Bom, acho que eu nem precisava ter falado isso já que explícito que esse blog está completamente jogado as moscas... mas fazer o que... nem todos podem ficar o dia inteiro escrevendo e atualizando blogs.

Segue um SS do md5check em funcionamento:

terça-feira, 5 de fevereiro de 2008

Acessando bibliotecas nativas com JNA (Parte 2)

ir para a Parte 1

Como prometido, essa é a segunda parte do artigo sobre JNA. Na primeira parte fizemos o acesso a uma biblioteca nativa de sistemas operacionais Linux. Nesse artigo faremos a comunicação com uma DLL e para isso utilizaremos o mesmo exemplo do site do projeto JNA.

Diferentemente do artigo anterior não vamos criar aqui nossa própria DLL para acesso, mas sim utilizar uma DLL já existente nos sistemas operacionais Windows: KERNEL32.DLL. Mesmo porque grande parte (senão todos) os software que utilizariamos para essa finalidade são pagos e não queremos isso para um simples exemplo. A versão do sistema operacional utilizado para esse exemplo é o Windows XP e não tenho a mínima idéia se em outras versões também funcionará.

Como as explicações sobre o que é JNA, por que utilizar, como e onde obter já foram discutidas na primeira parte desse artigo, vamos então direto para a parte de codificação pois o cenário é o mesmo e o JAR utilizado também será o mesmo.

O objetivo aqui será obter a data e a hora do sistema através de uma biblioteca nativa do sistema operacional em questão. Obviamente que para esse objetivo existem maneiras bem mais simples utilizando diretamente a linguagem Java, mas como o exemplo é apenas para fins didáticos, vamos obter essas informações através da DLL mesmo.

Como dito anteriormente, vamos utilizar uma biblioteca já existente chamada KERNEL32.DLL e que fica localizada no diretório de instalação do Windows e na subpasta SYSTEM32. Na maioria dos casos a instalação padrão fica no diretório \WINDOWS porém se você não tem certeza de qual a unidade e o seu diretório de instalação você pode via prompt de comando utilizar o seguinte comando:

echo %SystemRoot%

Bom, na realidade isso vale apenas como curiosidade pois a DLL já é acessível de qualquer parte do sistema operacional e não precisamos nos preocupar com isso. Portanto chega de papo furado e vamos 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 em relação ao exemplo do primeiro artigo. Aqui a interface 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 e 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 e agora resta a implementação da 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. Como não estamos trabalhando com packages devemos ter no mesmo diretório os três arquivos criados mais a biblioteca JNA (jna.jar):
javac -cp .;jna.jar Kernel32JNA.java

E executar:
java -cp .;jna.jar Kernel32JNA

No meu caso o resultado foi:
Data: 5/2/2008
Hora: 13:26

Uma vez que o relógio do meu sistema operacional marcava 10:26 eu imagino que o retorno seja o horário atual sem a aplicação do fuso-horário do Brasil, já que estamos atrasados 3 horas em relação ao Meridiano de Greenwich.

É isso aí, espero que tenha ficado claro e que tenham conseguido executar sem erros. Fiquem à vontade para perguntar, comentar ou criticar.

Para os curiosos de plantão, existe um exemplo bem legal utilizando o JNA que descobre dinamicamente qual o sistema operacional hospedeiro da JVM e cria um relógio em uma janela não-retangular (circular). O exemplo pode ser executado via Java Web Start aqui e o código-fonte para análise pode ser obtido aqui.

Shaped Window Demo sendo executado:

terça-feira, 29 de janeiro de 2008

Acessando bibliotecas nativas com JNA (Parte 1)

ir para a Parte2

Tempos atrás eu me ralei para integrar periféricos com minha aplicação Java. Tive que fazer integrações com emissor de cupons fiscais (ECF), com balança, com leitor ótico e por aí afora. É certo que a maioria deles fornecem bibliotecas de comunicação que facilitam a vida do desenvolvedor, mas como nem tudo é um mar-de-rosas me deparei com 2 problemas:

1) A maioria das empresas desenvolvem bibliotecas nativas para o sistema operacional Windows (DLLs) e simplesmente ignoram outros sistemas operacionais, matando assim o sonho da multiplataforma que o Java proporciona;
2) São poucos que desenvolvem bibliotecas nativas para 2 ou mais sistemas operacionais e muito menos os que disponibilizam algum JAR para facilitar nossa vida. Nesse caso a solução é utilizar o JNI (Java Native Interface) que por sinal é bem trabalhoso.

Infelizmente temos que conviver, AINDA, com o problema número 1. O problema 2, porém, foi amenizado pois hoje podemos contar com um projeto maduro e muito mais prático: o JNA (Java Native Access) cujo primeiro release é datado de 30 de novembro de 2006. Na data em que escrevi esse artigo, o JNA se encontra na versão 3.0

O objetivo desse artigo é mostrar um exemplo simples porém prático de como utilizar o JNA e integrá-lo com uma biblioteca nativa. Para tal vamos dividir este em duas partes, onde a primeira estaremos integrando com um Shared Object ou Shared Library (.so) que é uma biblioteca compartilhada de sistemas operacionais Linux similar as DLL's nos sistemas operacionais Windows. E a segunda parte estaremos integrando com uma DLL.

Como vamos criar nosso próprio programa em C e gerar uma biblioteca dinâmica, é necessário ter instalado em seu SO Linux um ambiente de desenvolvimento, com as bibliotecas padrão e o compilador gcc. No meu caso como eu uso o Ubuntu Linux bastou que eu instalasse o pacote build-essential(Também vou levar em consideração que você tem um ambiente Java corretamente configurado):
sudo apt-get install build-essential

Precisamos também fazer o download do JNA na seção de downloads do site oficial ou diretamente aqui. Salve esse arquivo no mesmo diretório que você vai trabalhar ou se você conhece sobre CLASSPATH no Java pode salvar onde quiser de forma que deveremos utilizá-la mais tarde.

Feito isso podemos começar a codificar. 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;
}

* a função strcmp acima retorna 0 (zero) caso o valor entre as Strings sejam o mesmo.

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);

Exatamente! Só isso mesmo. Agora para gerarmos a biblioteca compartilhada devemos executar o seguinte comando:
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, pois essa é uma exigência que devemos seguir. 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. Lembrando que diferentemente do JNI essa é a única exigência do JNA. 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 é, coicidentemente (ou não) 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);
}
}

Veja que em apenas uma linha de código carregamos a biblioteca através do método estático loadLibrary da classe Native. Repare também que devemos (no caso do Linux) 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, no entanto cabe antes algumas considerações sobre bibliotecas compartilhadas no Linux.

Existem alguns diretórios padrão onde devemos colocar nossa biblioteca compartilhada, entre eles o /usr/lib que simplesmente exigiria que criássemos um link simbólico para nosso arquivo real:
sudo ln -s calcjna.so /usr/lib/libcalcjna.so

Note que precisamos de privilégios de super-usuário para isso. Outra saída seria configurar dentro do arquivo /etc/ld.so.conf o diretório em que está nosso arquivo calcjna.so e muito provavelmente executar um ldconfig. Isso tudo também como super-usuário. Caso você não tenha essa permissão ou não queira por algum motivo utilizá-la você pode configurar uma variável de ambiente que indicará ao sistema operacional, qual o diretório que ele pode encontrar as bibliotecas compartilhadas. O comando para isso é :
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/diretorio

Ufa! Agora sim vamos voltar para o Java! 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

Se nenhuma mensagem de erro apareceu, resta apenas executar e ver a mágica acontecer. Para isso:
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
Se isso aconteceu PARABÉNS, você acessou de forma simples um código nativo através do Java, graças a JNA. Agora em caso de erro, verifique as possíveis causas e soluções abaixo.

ERRO: Unable to load library calcjna: libcalcjna.so: cannot open shared object file: No such file or directory
CAUSA: o sistema operacional não localizou a biblioteca que você está solicitando.
SOLUÇÔES:
- o nome da biblioteca gerada realmente é igual ao que está sendo exibido pela mensagem de erro, no caso: libcalcjna.so
- você configurou corretamente a variável de ambiente LD_LIBRARY_PATH
- você tem permissões de super-usuário e não utilizou a configuração de uma variável de ambiente, verifique se criou o link simbólico em um diretório que é buscado pelo comando ldconfig e certifique-se de ter executado o comando ldconfig

ERRO: Exception in thread "main" java.lang.NoClassDefFoundError: com/sun/jna/Library
CAUSA: o compilador ou interpretador Java não encontrou o arquivo jna.jar.
SOLUÇÃO: configurar corretamente o CLASSPATH conforme abordado no artigo.

ERRO: Exception in thread "main" java.lang.NoClassDefFoundError: CalcJNA
ERRO: Exception in thread "main" java.lang.NoClassDefFoundError: ICalcJNA
CAUSA: o compilador ou interpretador Java não encontrou os arquivos bytecodes necessários.
SOLUÇÃO: configurar corretamente o CLASSPATH conforme abordado no artigo.

Concluímos que a biblioteca JNA é muito mais fácil e produtiva do que a JNI. Como ainda não utilizei em produção não sei opinar sobre sua performance ou estabilidade, mas tudo indica que é um projeto maduro. Nesse primeiro artigo vimos como integrar uma aplicação Java com uma biblioteca nativa, utilizamos o sistema operacional Linux e no próximo artigo mostrarei um exemplo acessando uma DLL do sistema operacional Windows.

Gostaria de agradecer e dedicar esse primeiro artigo ao Daniel Fernandes Martins do batteries not included que me incentivou a criar o blog e ao Marcos Antônio Assis da Onclick Sistemas que ajudou nos códigos C.