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 postMinha 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.
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.
- /* DO NOT EDIT THIS FILE - it is machine generated */
- #include <jni.h>
- /* Header for class OlaMundo */
-
- #ifndef _Included_OlaMundo
- #define _Included_OlaMundo
- #ifdef __cplusplus
- extern "C" {
- #endif
- /*
- * Class: OlaMundo
- * Method: saudacoes
- * Signature: ()V
- */
- JNIEXPORT void JNICALL Java_OlaMundo_saudacoes
- (JNIEnv *, jobject);
-
- #ifdef __cplusplus
- }
- #endif
- #endif
-
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.
- /*OlaMundo.c*/
-
- #include <jni.h>
- #include
- <olaMundo.h>
-
- JNIEXPORT void JNICALL Java_OlaMundo_saudacoes
- (JNIEnv *, jobject){
- return;
- }
-
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 commentsJBOSS – 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.
Feito isso vai veremos a tela abaixo:
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





