SemtexGirl

É uma garota dinâmica e de bom gosto, uma garota que explode, que não para e nem aceita um não como resposta.

Archive for dezembro, 2006

Assinatura ou descritores de argumento

Para descrever os tipos de um atributo ou dos métodos de um objeto, a linguagem Java utiliza as assinaturas de argumento, também conhecida como descritores de membros (member descriptor).

Uma assinatura de argumento é uma string C que contem o tipo de um membro (atributos ou métodos) da uma classe.

ex.:
“Ljava/lang/String;” indica que é um atributo do tipo java.lang.String

Note que os tipos primitivos são representados apenas com letras. As assinaturas de argumento (ou descritores) são definidas a seguir:

Caractere
BaseType
Tipo Descrição
B byte byte assinalado
C char caractere Unicode
D double valor de ponto de flutuação de precisão dupla
F float valor de ponto de flutuação de precisão única
I int inteiro
J long inteiro longo
L; Reference uma instância da classe
S short short assinalado
Z boolean true ou false
[ Reference uma dimensão de matriz

Para saber mais detalhes, consulte o capítulo 4.3.2, Field Descriptors, na Especificação do Sun VM

Por exemplo, para representar o tipo int usamos “I” e para representar o tipo float “F”.

Porem para representar um array usamos um “[” ao lado do tipo.

ex.:
para representar um int[] usamos “[I” e
para representar um double[][] usamos “[[D”.

Por último para representarmos uma classe usamos o prefixo L seguido do nome completo da classe terminando com “;“.

ex.:
para representarmos uma classe java.util.Date usamos
“Ljava/util/Date;”

Note que substituíamos os . por /.

Assim como os atributos, os métodos também possuem uma assinatura de argumento e neste caso para representá-los colocamos primeiro os argumentos do método entre parênteses e depois o seu retorno.

ex.:
Para o método: public int getWidth();
Teremos como assinatura de argumento: “()I” e não “(V)I”.

Ou ainda para o método: public void getWidth(int w);

Teremos como assinatura “(I)V”, isso é para mostrarmos que na assinatura do retorno podemos colocar V.

Outro exemplo um pouco mais complexo:

public java.lang.String formatDate(java.util.Date)

Sua assinatura seria do tipo “(Ljava/util/Date;)Ljava/lang/String;”

Se um método receber vários parâmetros podemos colocá-los um seguido do outro sem nenhum separador:

ex.:
public int soma(int a, int b)
Sua assinatura seria “(II)I”

Agora vamos imaginar que tenhamos recebido uma classe Java, ou que não saibamos qual a assinatura para determinado método ou atributo, como podemos descobri-lo?

A melhor maneira de fazê-lo é utilizando o javap, um desassemblador de classes Java, o javap consegue abrir e ler uma classe Java e listar as assinaturas de argumento de seus atributos e métodos, entre outras coisas.

O javap fica na pasta bin do seu JDK e podemos utilizá-lo da seguinte forma:

javap[-opções] minhaClasse

Sendo que para listar as assinaturas de argumento usamos a opção –s, que informa ao javap que o mesmo deve mostrar as assinaturas no formato JNI e não as assinaturas no formato Java, e para incluímos as assinaturas dos métodos e atributos do tipo private usamos a opção –p.

Caso queria saber um pouco mais sobre o javap consulte sua documentação no site da SUN.

ex.:
Executando javap para a classe InstanceMethodCall:

% javap -s -p InstanceMethodCall

Teríamos como saída:

…
private callback ()V
public static main ([Ljava/lang/String;)V
private native nativeMethod ()V
…	

Uma vez que tenhamos a assinatura do argumento podemos passá-la para as funções como, por exemplo:

FindClass
Protótipo:

tipo_array = (*env)->FindClasse(env, “[I”);

Agora que já sabemos um pouco mais sobre as assinaturas de argumento vamos para o próximo passo "Acessando os atributos de um objeto".

Comments are off for this post

Minha primeira tentativa de artigo técnico

Há um bom tempo atrás comecei a escrever um post sobre JNI que acabou virando um esboço de um artigo que não saiu do 'papel', deixei ele perdido no meu computador e só agora quase 4 meses depois resolvi colocá-lo aqui mesmo no blog, o artigo é enorme então vou postar um pedacinho a cada dia, assim tenho tempo para revisá-lo e de bem, se alguem mais ler posso saber se consegui me fazer entender.

JNI - Rompendo as barreiras do acesso às aplicações nativas I

Há pouco tempo precisei desenvolver uma aplicação Java integrada com funções nativas e me vi perdida em meio de uma série de complicações na hora de utilizar JNI. Infelizmente, na época, não encontrei nenhuma documentação que se aprofundasse no assunto. Principalmente quando este era envio/manipulação de estrutura/objetos, conversei com outros desenvolvedores na empresa em que trabalhava e todos me aconselharam a fazer algum ‘ajuste técnico’ no lugar de trabalhar com o envio de objetos. Apesar de esta sugestão ser a saída mais fácil, não me pareceu nada elegante retornar um string concatenando todos os dados da estrutura, ou criar métodos auxiliares que me retornariam cada atributo da estrutura em C separadamente.
Depois de quebrar a cabeça para entender melhor o funcionamento do JNI finalmente consegui trabalhar com objetos Java dentro do C, e é isso que vou tentar explicar abaixo. Mas antes foi explicar um pouco sobre o que é e como funciona o JNI sem no entendo me estender muito, pois estas explicações se acham facilmente digitando JNI no Google, vou tentar explicar como enviar e trabalhar com os objetos Java em C.

Introdução

Em Java para nos comunicarmos com bibliotecas nativas utilizamos o JNI uma interface de comunicação desenvolvida pela Sun que permite de forma bidirecional que os códigos executados pela Java Virtual Machine (JVM) acessem aplicações e bibliotecas desenvolvidas em outras linguagens, como C, C++ e Delphi como se fossem métodos Java, facilitando, por exemplo, a integração de sistemas legados.

JNI 1

Figura1 : Representação das camadas de comunicação

Porem existe algumas premissas para a utilização de JNI são elas:

1. Implementação de funcionalidades que as API's Java não fornecem
2. Problemas de desempenho.
ex.
Um algoritmo que em Java fique muito lento pode ser implementado em C ou Assembly e acessado pelo Java.
3. Ou ainda, quando o código nativo deseja ter acesso à API Java.

O desenvolvimento de um programa Java que chama funções nativas pode ser dividido em seis passos:

1. O primeiro passo é escrever o código Java que execute três tarefas;

Declaração do método nativo que estaremos chamando;
Carga da biblioteca compartilhada que contém o código nativo;
E que execute uma chamada ao método nativo.

Como exemplo, criaremos uma aplicação Java que execute um método nativo que imprima a mensagem “Olá Mundo!”.

Listagem 1 : OlaMundo.java

Como podem notar na listagem 1, os métodos nativos carregam o modificador native, e sua função nativa correspondente será implementada na forma de uma biblioteca dinâmica conforme veremos em breve.

Notamos ainda a existência do método:

void <system>.loadLibrary(String libraryName)
 

Observe que ele recebe como parâmetro o nome da biblioteca dinâmica que iremos utilizar. A biblioteca deve ser carregada antes da chamada de qualquer método nativo.

2. Compilar o código Java;

javac OlaMundo.java

3. Criar o arquivo de header C/C++. O arquivo de header C/C++ declarará a assinatura da função nativa que queremos chamar. Este header será usado durante a implementação da função C/C++ (veja Passo 4) onde criaremos a nossa biblioteca dinâmica.

javah OlaMundo

Lembre-se que OlaMundo é o nome da sua classe Java já compilada a partir do qual geraremos o arquivo OlaMundo.h, como podem ver na listagem 2.

  1. /* DO NOT EDIT THIS FILE - it is machine generated */
  2. #include <jni.h>
  3. /* Header for class OlaMundo */
  4.  
  5. #ifndef _Included_OlaMundo
  6. #define _Included_OlaMundo
  7. #ifdef __cplusplus
  8. extern "C" {
  9. #endif
  10. /*
  11.  * Class: OlaMundo
  12.  * Method: saudacoes
  13.  * Signature: ()V
  14.  */
  15. JNIEXPORT void JNICALL Java_OlaMundo_saudacoes
  16. (JNIEnv *, jobject);
  17.  
  18. #ifdef __cplusplus
  19. }
  20. #endif
  21. #endif
  22.  

Listagem 2: OlaMundo.h

4. Escrever o código nativo. Este passo consiste em implementar a função desejada em C/C++, notem que no código da listagem 3 incluímos o arquivo de header OlaMundo.h criado no passo anterior.

  1. /*OlaMundo.c*/
  2.  
  3. #include <jni.h>
  4. #include
  5. <olaMundo.h>
  6.  
  7. JNIEXPORT void JNICALL Java_OlaMundo_saudacoes
  8. (JNIEnv *, jobject){
  9. printf("Olá Mundo!\n");
  10. return;
  11. }
  12.  

Listagem 3 : OlaMundo.c

5. Criar o arquivo de biblioteca dinâmica.

Agora que todo o código necessário foi escrito, podemos compilar a biblioteca nativa. Ela será invocada dinamicamente quando a classe OlaMundo tiver o método saudacoes() invocado. Para efetuar a compilação devemos considerar o sistema operacional e o compilador utililizado.

Se estivermos utilizando o Windows e o Microsoft Visual Studio, utilizaremos o comando abaixo:

cl -Ic:\jdk1.5.0_07\include -Ic:\jdk1.5.0_07\include\win32 -MD -LD OlaMundo.c OlaMundo.dll

Já em Solaris poderíamos utilizar:

cc -G -I/java/include -I/java/include/solaris OlaMundo.c -o libOlaMundo.so

E em Linux o comando correspondente seria:

gcc -o libOlaMundo.so -shared -Wl,-soname, libOlaMundo.so -I/export/home/java/include -I/export/home/java/include/linux OlaMundo.c -static –lc

Concluído o passo 5 teremos uma aplicação JNI pronta esperando para ser executada.

6. Executar o programa.

Neste ponto, têm-se dois componentes prontos para serem executados. O arquivo “.class” (OlaMundo.class) chama o método nativo e a biblioteca nativa (olamundo.dll ou libOlaMundo.so) implementa o método nativo. Como a classe OlaMundo contém sua própria função main, ela pode ser executada da seguinte maneira:

> java OlaMundo

Durante a execução do programa podemos obter algumas mensagens de erro, sendo que as três mais comuns são:

1. Uma ligação dinâmica (dynamic link) não pode ser encontrada. Isto resulta na mensagem de erro: java.lang.UnsatisfiedLinkError. Normalmente, esta mensagem significa que ou o arquivo de biblioteca dinâmica, ou um método nativo específico dentro da biblioteca dinâmica não pode ser encontrado.

2. O arquivo de biblioteca dinâmica não pode ser encontrado. Isso acontece quando a JVM não consegue localizar o arquivo de biblioteca indicado no loadLibrary(), caso isto ocorra devemos verificar se o nome do arquivo esta escrito corretamente e que não se tenha especificado a extensão. Devemos, também, verificar se o arquivo de biblioteca encontra-se acessível à JVM. Caso desejemos utilizar outros caminhos, que não os padrões, existem duas técnicas. A primeira consiste em setar a variável de ambiente PATH, no Windows, ou LD_LIBRARY_PATH, para Linux e Solaris, com o caminho desejado. E a outra é setar este caminho através de um parâmetro, durante a chamada do programa java:

java -Djava.library.path=. OlaMundo

Ola mundo!

No caso o parâmetro –D informa à máquina virtual que existe uma variável de ambiente do Java chamada java.library.path e ela será analisada pela máquina virtual como outro caminho alternativo aonde ir buscar as bibliotecas dinâmicas e até mesmo outras classes utilizadas no projeto.

No exemplo acima estamos informando que o arquivo de biblioteca dinâmica encontra-se no diretório atual.

3. Um método com a assinatura especificada não pode ser encontrado. A implementação da função C/C++ deve possuir uma assinatura idêntica à assinatura da função no arquivo de header. Se quiser saber um pouco mais sobre assinatura/descritor de argumento é só ler o próximo capítulo e este não for o seu caso vá direto para o capitulo “Acessando os atributos de um objeto”.

Agora que já relembramos os passos para a criação de uma aplicação JNI, podemos nos aprofundar um pouco mais sobre seu funcionamento.

O próximo post será sobre assinatura ou descritores de argumento.

6 comments

Configurando multiplas instancias do JBOSS em uma única máquina

Alguns devem se perguntar qual a necessidade de ter mais de uma instância do JBOSS na mesma máquina? Pois bem, a minha necessidade é ter duas instâncias com JDKs distintos, ou seja, uma versão com o Java 1.4 e a outra com o Java 5. Esta necessidade se fez presente durante um periodo de migração do Java em nosso ambiente de desenvolvimento e homologação e ainda o desenvolvimento de uma nova aplicação já usando o Java 5.

A princípio fiquei meio perdida com as diversas informações que encontrei na internet sobre o assunto, mas no final deu tudo certo e a solução adotada por mim foi disponibilizada no próprio site do JBOSS, como poderão ver a solução é bastante simples, consiste em basicamente duplicar o server default com o nome desejado e descomentar a TAG

 
<mbean code=”org.jboss.services.binding.ServiceBindingManager”
name=”jboss.system:service=ServiceBindingManager”>
 

do arquivo <JBOSS>/server/java5/conf/jboss-service.xml para ler o arquivo <JBOSS>/docs/examples/binding-manager/sample-bindings.xml, este XML contêm a configuração das portas a serem utilizadas pelo seu server, afinal se simplesmente executarmos o script de inicialização indicando novo server este tentará utilizar as portas padrões, ocasionando erros. Deste modo para evitarmos este tipo de problema é preciso indicar ao server as portas que ele deve utilizar, eu fiz o jeito simples, editei o arquivo <JBOSS>/server/java5/conf/jboss-service.xml, retirando o comentário da TAG e alterei o ServerName para ports-02 conforme abaixo:

 
<mbean code=”org.jboss.services.binding.ServiceBindingManager”
name=”jboss.system:service=ServiceBindingManager”>
<attribute name=”ServerName”>ports-02</attribute>
<attribute name=”StoreURL”>${jboss.home.url}/bindings.xml</attribute>
<attribute name=”StoreFactoryClassName”>
org.jboss.services.binding.XMLServicesStoreFactory
</attribute> </mbean>
 

Isto faz com que o server no momento do start busque as portas na configuração definidas como ports-02 no arquivo jboss-service.xml.

E pronto! Tudo funcionando ;o) Como eu disse no inicio simples. Para rodar os servers com os JDKs distintos alterei o script de inicialização indicando qual o JDK que eu desejo usar e problema resolvido.

2 comments

JBOSS – Remote Debugging

O JBOSS é de longe o application server mais simples de usar mesmo assim as vezes me pego perdida na hora de ajustá-lo as minhas necessidades, uma delas foi acesso remoto, infelizmente no ambiente de desenvolvimento que trabalho nem sempre é possível debugar a aplicação localmente, então lá fui eu ver como isso funciona...

Passo 1:
Ajustar .bat/sh de start do JBOSS, eu uso a versão 4.0.5 e não sei se tem alguma diferença com as demais...

Localize a frase "JPDA options. Uncomment and modify as appropriate to enable remote debugging" é só descomentar a linha abaixo para habilitar debug remoto, no meu caso tive ainda que alterar a opção suspend=y para suspend=n, a opção address indica a porta pela qual você se conectara para debugar o projeto.

Feito isso é só dar um stop/start no JBOSS que ele esta prontinho.

Passo 2:
Vá até o eclipse, clique com o botão direito do mouse em cima do projeto que deseja debugar e vá até a opção Debug As -> Debug.

Debug As

Feito isso vai veremos a tela abaixo:

Remote

Agora é só colocar endereço que quer acessar e a porta que foi configurada para debug no JBOSS. Agora é só clicar de DEBUG e abrir um browser digitar a URL desejada e pronto ;o) a perspectiva de debug será ativada no primeiro break point.

Comments are off for this post