PONTIFÍCIA UNIVERSIDADE CATÓLICA DE MINAS GERAIS
Departamento de Ciência da Computação (Campus Poços de Caldas)
Desenvolvimento de um weaver dinâmico distribuído baseado em comunicação de grupo
Alex de Melo Silva
Poços de Caldas
2007
Alex de Melo Silva
Desenvolvimento de um weaver dinâmico distribuído baseado em comunicação de grupo
Trabalho apresentado a disciplina de Estágio Supervisionado do Departamento de Ciência da Computação da Pontifícia Universidade Católica de Minas Gerais (Campus Poços de Caldas).
Poços de Caldas
2007
FICHA CATALOGRÁFICA ELABORADA PELA
BIBLIOTECA DA PUC Minas (Campus Poços de Caldas)
Alex de Melo Silva
Desenvolvimento de um weaver dinâmico distribuído baseado em comunicação de grupo
Trabalho apresentado à disciplina de Estágio Supervisionado da Pontifícia Universidade Católica de Minas Gerais, Poços de Caldas, 2007
___________________________________________________
Prof. Dr. Udo Fritzke Jr. (Orientador(a)) – PUC Minas
Agradecimentos
Primeiramente agradeço a Deus por tudo que consegui até agora e por sempre ter motivação e condições para superar todos os obstáculos.
Agradeço muito a minha família por todo apoio tanto moral quanto financeiro, e em especial meu Pai por todo esforço pra me fazer chegar até aqui.
Agradeço meu Orientador Prof. Udo Fritzke Jr. pela confiança e credibilidade que me permitiram participar deste trabalho
Agradeço a minha amiga Ana Paula Leal pela força que me deu em momentos difíceis e compreensão durante o desenvolvimento desta monografia.
Agradeço a minha professora Maria Adriana Vidigal de Lima pelo apoio de paciência de me ajudar com a ferramenta usada no desenvolvimento deste documento.
Resumo
Este trabalho apresenta um estudo detalhando sobre duas ferramentas utilizadas na migração e evolução de um Middleware Adaptável para Computação Móvel, implementado em um trabalho anterior utilizando a ferramenta ApectJ e programação orientada a Aspectos, para a implementação e tratamento isolado dos protocolos de controle de concorrência e replicação de dados.
A ferramenta AspectWerkz é o framework escolhido para a migração dos aspectos, devido ao seu mecanismo de weaver dinâmico que permite a alteração do fluxo de execução do código em tempo de execução.
A ferramenta JGroups é um poderosa ferramenta para comunicação de grupo confiável, ou seja, garante que as mensagens cheguem para cada destinatário respeitando a ordem estipulada. Utilizada pelo middleware para trocas de mensagens multicast dentro do sistema transacional do trabalho.
Abstract
Esta seção apresenta a versão do resumo em idioma de divulgação internacional. (Abstract em Inglês)
Sumário
1 Introdução. xii
2 JGroups. 13
2.1 Canal 14
2.1.1 Endereço. 16
2.1.2 Mensagem.. 16
2.1.3 Visão. 18
2.2 Blocos de Construção. 19
2.2.1 PullPushAdapter 19
2.2.2 MessageDispatcher 20
2.2.3 RpcDispatcher 20
2.2.4 DistributedHashtable. 21
2.2.5 ReplicatedHashtable. 21
2.2.6 DistributedTree. 21
2.2.7 NotificationBus. 22
2.3 Pilha de Protocolo. 22
2.4 Interfaces. 23
2.4.1 Transport 23
2.4.2 MessageListener 23
2.4.3 MembershipListener 24
2.4.4 ChannelListener 24
2.4.5 Receiver 24
2.5 Classes úteis. 24
2.5.1 objectToByteBuffer(), objectFromByteBuffer() 24
2.5.2 printMessage() 25
2.5.3 activeThreads() 25
2.5.4 printMembers() 25
3 AspectWerkz. 26
3.1 Aspectos. 27
3.2 Advice. 28
3.2.1 after finally (after) 28
3.2.2 after returning [TIPO] 29
3.2.3 after throwing [TIPO] 29
3.3 Pointcuts. 29
3.4 Definição XML.. 30
3.4.1 Definição de Aspectos. 30
3.4.2 Definição de Pointcuts. 31
3.4.3 Definição de Advices. 31
3.5 Passagem de parâmetros para o aspect 32
3.6 Programação Orientada a Aspectos Dinâmica. 32
3.6.1 Hot deployment 33
3.6.2 Hot undeployment 33
3.6.3 Deployment scopes. 33
3.6.4 Deployment handles. 34
Referência Bibliográfica. 35
Lista de Figuras
Figura 1 - Arquitetura do JGroups. 12
Figura 2 - Estados de um Canal 14
Figura 3 - Estrutura de Mensagem.. 16
Lista de Abreviaturas
LAN
Local Area Network
WAN
World Area Network
API
Application Programming Interface
BSD
Berkeley Software Distribution
UDP
User Datagram Protocol
POA
Programação Orientada a Aspecctos
XML
eXtensible Markup Language
JVM
Java Virtual Machine
JVMTI
ava Virtual Machine Tools Interface
1 Introdução
A replicação de objetos em sistemas transacionais é realizada pela disseminação de cópias destes objetos em processos diferentes de um sistema distribuído. Esta replicação garante a disponibilidade de informações, mesmo em casos de falha de nós e canais de comunicação do sistema [Caproni, 2005]. Com toda replicação de dados, faz-se necessário protocolos de replicação e protocolos de controle de concorrência, para que as informações sempre estejam coerentes em todo o sistema. Na prática a implementação de protocolos desta natureza possui algoritmos de implantação extremamente complexa e existe muito de qualquer programador.
A Programação Orientada a Aspectos surgiu como paradigma para resolver problemas em que a programação Orientada a Objetos trata com muita dificuldade. Entre esses problemas, o controle de concorrência e replicação de dados.
Com a POA foi implementado um Middleware Adaptável para Computação Móvel (implementado por [Caproni, 2005]). O Middleware foi implementado usando este paradigma e tanto o controle de concorrência como o controle de replicação foram implementados em aspectos.
O Middleware foi implementado com AspectJ utilizando POA estática, porém foi migrado para a ferramenta AspectWerkz devido ao seu mecanismo de weaver dinâmico que permite alteração no fluxo de execução do Middleware durante o tempo de execução. Após a migração uma nova ferramenta foi introduzida ao projeto para fazer a comunicação entre as diversas máquinas em um sistema transacional. A ferramenta escolhida foi a ferramenta JGroups, trata-se de um framework para comunicação em grupo confiável. Este trabalho oferece um estudo detalhado sobre estas duas ferramentas utilizadas no desenvolvimento e evolução do Middleware até aqui.
2 JGroups
JGroups é uma ferramenta para comunicação de grupo confiável [JGroups, 2006]. Os processos podem conectar-se a um grupo, enviar mensagens para todos os membros ou para apenas um, e receber mensagens de todos os membros do grupo. O sistema mantém uma lista de todos os membros do grupo, e notifica sempre quando um novo membro conecta ou quando um membro no grupo falha. A identificação de cada grupo é feita pelo seu nome. Os grupos não necessitam ser criados explicitamente, ou seja, quando um processo se conecta em um grupo ainda não-existente, o grupo será criado automaticamente. Os processos de um membro podem estar no mesmo host, dentro da mesma rede (LAN), ou em qualquer lugar no mundo (WAN). Um membro pode fazer parte de vários grupos [JGroups, 2006].
A arquitetura do JGroups é apresentada na figura 1:
Figura 1 - Arquitetura do JGroups
A arquitetura do JGroups é formado por três partes.
1. A API Channel (Canal), que é usada pelos programadores para desenvolver aplicações de comunicação de grupo confiável [JGroups, 2006].
2. Os Building Blocks (Blocos de Construção), é encontrada entre a camada da API Channel e a camada de Aplicação. Os blocos de construção proporcionam um alto nível de abstração.
3. A Protocol Stack (Pilha de Protocolos), que implementa as propriedades especificadas por um canal [JGroups, 2006].
Um canal está conectado a pilha de protocolo [JGroups, 2006]. Sempre que uma aplicação envia uma mensagem, o canal envia a mensagem para o topo da pilha de protocolo. O protocolo processa a mensagem e envia para o protocolo de baixo. Assim a mensagem é enviada de protocolo a protocolo até chegar ao protocolo mais baixo que envia a mensagem para a rede. O mesmo acontece no sentido contrário, o protocolo mais baixo aguarda por mensagens na rede, quando uma mensagem é recebida ela será passada para os outros protocolos até atingir o canal. O canal armazena a mensagem em uma fila até que a aplicação a consuma [JGroups, 2006].
Quando uma aplicação conecta-se no canal, a pilha de protocolo é inicializada, e quando a aplicação desconecta-se a pilha é finalizada. Quando um canal é fechado, a pilha será destruída a seus recursos serão liberados [JGroups, 2006].
2.1 Canal
Para entrar em um grupo e enviar mensagens, um processo deve criar um canal [JGroups, 2006]. Após a criação do canal o cliente deve conectar-se a ele e indicar o nome do grupo que deseja conectar. Portanto, um canal no seu estado conectado está sempre associado a um grupo. Sempre que um cliente conecta-se em um canal informando o nome do grupo, a pilha de protocolos procura por grupos com o mesmo nome, e se encontrar este cliente fará parte do grupo, o que resulta em uma nova visão. Se não encontrar outros canais com o mesmo nome de grupo um novo grupo é criado.
Um canal é criado sempre no estado desconectado. A tentativa de fazer operações possíveis apenas no estado conectado, como enviar e receber mensagens, dispara um exceção. Após uma conexão bem sucedida o cliente passa para o estado conectado [JGroups, 2006]. Enquanto estiver conectado, um membro pode enviar e receber mensagens de todos os membros do grupo. Quando um canal é desconectado ele passa para o estado desconectado [JGroups, 2006]. Qualquer canal conectado ou desconectado pode ser fechado, limitando-o à apenas algumas operações. Qualquer operação ilegal neste estado dispara um exceção. Quando um cliente é desconectado diretamente do estado conectado, primeiro ele é desconectado e depois fechado [JGroups, 2006].
Os estados que um canal pode assumir é mostrado no diagrama abaixo:
Figura 2 - Estados de um Canal
Cada canal possui um único endereço [JGroups, 2006]. Os canais sempre sabem quem são os outros membros do mesmo grupo. Uma lista de endereço dos membros pode ser adquirida de qualquer canal [JGroups, 2006]. Esta lista é chamada de view (visão). Sempre que um processo entra ou sai de um grupo, ou quando um processo travado é encontrado, uma nova visão é enviada para todos os outros membros do grupo [JGroups, 2006]. Quando o processo de um membro é suspeito de ter falha, uma mensagem de suspeita é enviada para todos os membros ativos. Assim, o canal recebe mensagens regulares, mensagens de visão e mensagens de suspeita [JGroups, 2006].
Os canais são similares BSD sockets. As mensagens são armazenadas em um canal até que um cliente a remova. Quando não houver mensagens disponíveis o cliente é bloqueado até que uma mensagem seja recebida [JGroups, 2006].
Um canal pode ser implementado sobre várias alternativas de transporte de grupo. Um canal é somente uma classe abstrata, de onde são derivadas implementações concretas. As propriedades de um canal são especificadas em formato string. Quando um canal é criado, a pilha de protocolo será criada de acordo com estas propriedades. Todas as mensagens passam pela pilha, garantindo a qualidade do serviço especificado pela string de propriedades para um determinado canal [JGroups, 2006].
2.1.1 Endereço
Cada membro do grupo possui um endereço único que o identifica. A interface para um endereço é a Adress que requer implementações concretas para proporcionar métodos para comparação e ordenação de endereços e para determinar se um endereço é endereço \ multicast [JGroups, 2006].
2.1.2 Mensagem
Os dados são enviados entre os membros em formato de mensagens. Um mensagem pode ser enviada por um membro a outro ou para todos os outros membros do grupo [JGroups, 2006].
A figura a seguir mostra a estrutura de uma mensagem:
Figura 3 - Estrutura de Mensagem
A estrutura de uma mensagem contém cinco partes:
· Destination address (Endereço de Destino): É o endereço do destinatário. Se for null a mensagem será enviada para todos os membros do grupo [JGroups, 2006].
· Source address (Endereço de Envio): Endereço do remetente, pode ser null, mas será preenchido pelo protocolo de transporte quando a mensagem for posta na rede [JGroups, 2006].
· Flags: É um byte usado para marcação.
· Payload (buffer): É a informação que a mensagem transporta.
· Headers (Cabeçalho): Uma lista de cabeçalhos que podem ser anexados a mensagem [JGroups, 2006].
Uma mensagem é parecida com um pacote IP e consiste em dados, endereço de origem e endereço de destino. Qualquer mensagem colocada na rede pode ser roteada para o seu destino e replicas podem ser retornadas ao endereço de origem [JGroups, 2006].
Normalmente não precisa preencher o endereço de origem quando se envia uma mensagem; isto é feito automaticamente pela pilha de protocolo quando a mensagem é colocada na rede. Porém pode haver caso em que o remetente queira preencher outro endereço de origem ao invés do seu próprio endereço, de forma que por exemplo, uma resposta seja enviada para outro membro [JGroups, 2006].
O Endereço de destino pode ser um endereço, determinando que o endereço de um membro pode ser determinado por uma mensagem recebida previamente ou pode ser null, que permite que a mensagem seja enviada para todos os membros do grupo [JGroups, 2006].
2.1.3 Visão
Uma visão (View) é uma lista com os atuais membros do grupo. Consiste em um identificador (ViewId) que identifica unicamente a visão e a lista de membros. As visões são atualizadas automaticamente pela pilha de protocolos sempre que um membro conecta no grupo ou quando um membro do grupo desconecta ou falha. Todos os membros do grupo recebe a mesma seqüência de visões [JGroups, 2006].
Há uma função de comparação que ordena todos os membros do grupo de uma mesma maneira. Usualmente o primeiro membro da lista é o coordenador (escolhido para emitir novas visões). Assim, sempre que um conjunto de membros muda, todo membro pode identificar o coordenador facilmente sem a necessidade de contactar outros membros [JGroups, 2006].
2.1.3.1 ViewId
O (ViewId) é usado somente para numero de visões. É formado pelo endereço do coordenador e uma seqüência de números [JGroups, 2006].
2.1.3.2 MergeView
Toda vez que um grupo é dividido em subgrupos, como por exemplo devido a uma divisão na rede e mais tarde os subgrupos se juntam novamente, uma MergeView será recebido pela aplicação e não uma visão [JGroups, 2006]. O MergeView é uma subclasse de Visão e contém uma lista de visões que foram unidas.
2.2 Blocos de Construção
Os canais são simples e primitivos. Eles oferecem funcionalidades limitadas de comunicação de grupo, e foram projetados após os modelos de BSD sockets, que foram extensamente usados e são bem conhecidos. A rasão é que uma aplicação pode usar apenas uma parte do JGroups, sem precisar incluir um conjunto inteiro de classes sofisticadas, que podem ou não ser uteis. Assim, uma mínima interface fica simples para o entendimento. O cliente precisa conhecer no mínimo doze métodos para ser capaz de criar um canal (mas frequentemente apenas usará de três a quatro métodos) [JGroups, 2006].
Os Blocos de Construção são localizados entre o Canal e a camada de Aplicação. A maioria não precisa de um canal, tudo que eles precisam é uma classe que implementa a interface Transporte. Isto permite que os Blocos de construção trabalhem em qualquer tipo de grupo de transporte que obedece esta interface. Os Blocos de construção podem ser usados no lugar dos canais sempre que uma interface de alto nível é requisitada. Os blocos de construção proporcionam uma API mais sofisticada. Em alguns casos, os blocos de construção podem oferecer acesso ao canal de baixo, se o bloco de construção não oferecer determinada funcionalidade, o canal pode ser acessado diretamente. Os blocos de construção são localizados no pacote org.jgroups.blocks.
2.2.1 PullPushAdapter
Um PullPushAdapter é sempre criado acima da classe que implementa a interface transporte como a Canal por exemplo. Os clientes interessados em ser notificados quando uma mensagem é recebida podem registrar com o PullPushAdapter usando o métotodo setLister() [JGroups, 2006]. Os clientes devem implementar também a interface MessageListener, onde o método receive() será chamado quando uma mensagem chega. Quando um cliente está interessado em receber uma visão, mensagem de suspeita ou blocos, eles devem adicionar um registro ao MembershipListener usando o método setMembershipListener(). Sempre que uma visão, suspeita ou bloco é recebida, o método correspondente será chamado [JGroups, 2006].
Uma instancia de PullPushAdapter cria uma \textit{thread} que constantemente chama o método receive(), bloqueando até uma mensagem estar disponível. Quando uma mensagem é recebida o método receive() é chamado.
2.2.2 MessageDispatcher
Canais são simples padrões de envio e recebimento de mensagens dessincronizados. Entretanto, um significante número de padrões de comunicação em comunicação de grupo requerem comunicação sincronizada. Por exemplo, um cliente deseja enviar uma mensagem para todo o grupo e esperar por todas as respostas. Ou outra aplicação deseja enviar uma mensagem para o grupo e esperar até que a maioria dos destinatários enviem a resposta, ou até um timeout ocorrer [JGroups, 2006].
MessageDispatcher oferece uma combinação de padrões e proporciona envio de mensagens sincronizadas (e também dessincronizadas) com correlação requisição-resposta [JGroups, 2006].
Uma instancia de MessageDispatcher é criada junto com um canal como um argumento e pode ser usado com cliente ou servidor: um cliente envia requisições e recebe respostas e um servidor recebe requisições e envia respostas. MessageDispatcher permite que a aplicação seja os dois ao mesmo tempo [JGroups, 2006].
2.2.3 RpcDispatcher
Esta classe é derivada da MessageDispatcher. Ela permite que um programador invoque métodos remotos em todos (ou um) grupo de membros e pode opcionalmente aguardar por retornos de valores. Uma aplicação irá simplesmente criar um canal no topo da camada do bloco de construção RpcDispatcher, que permite o envio de métodos remotos (cliente) e ao mesmo tempo ser chamado por outros membros (servidor) [JGroups, 2006].
Um bloco de construção RpcDispatcher reduz o código necessário para escrever uma aplicação de RPC baseada em comunicação de grupo, proporcionando um alto nível de abstração entre a aplicação e os canais.
2.2.4 DistributedHashtable
Um DistributedHashtable é derivado de java.util.Hashtable e permite criar diversas instancias de hashtables em diferentes processos . Todas essas instancias tem o mesmo estado o tempo topo. Quando uma instancia é criada, um nome de grupo determina a qual grupo de hashtables erá se conectar. A nova instancia irá listar o estado dos membros existentes e se autoconfigurar antes de iniciar o serviço de requisições. Se a instancia não encontrar membros ela inicializará com estado vazio [JGroups, 2006].
Sabendo que as chaves e valores de um hashtables serão emitidos através da rede como cópias, tanto as chaves como os valores devem ser serializáveis [JGroups, 2006]. Um DistributedHashtable permite que registro por notificações, como por exemplo quando um novo item é modificado ou removido. Todos os listeners serão notificados quando um evento do tipo alteração ou remoção ocorrer. A notificação é sempre local [JGroups, 2006].
2.2.5 ReplicatedHashtable
ReplicatedHashtable oferece os mesmos métodos da DistributedHashtable. Porém, não é implementada da mesma maneira. A ReplicatedHashtable usa comunicação assíncrona para manter as réplicas atualizadas.
2.2.6 DistributedTree
Assim como o DistributedHashtable esta classe proporciona estrutura de replicação de dados através de múltiplos processos [JGroups, 2006]. Entretanto a replicação é em estrutura de arvore. As informações são propagadas para todos os membros do grupo de maneira confiável e na mesma ordem utilizando canal.
A arvore é formada pela raiz e pode contar vários ou nenhum nó filho. Cada nó pode ser uma sub-arvore ou um nó folha. Um nó possui nome e valor e o valor pode ser qualquer objeto, desde que seja serializável. Um nó é identificado pela concatenação de todos os nós da raiz até ele separados pelo caracter '/' (Ex. a/b/c) [JGroups, 2006].
2.2.7 NotificationBus
Esta classe proporciona notificação de envio e capacidade de controle. Também permite ao programador manter um cache local que é replicado para todas as instâncias. NotificationBus também está acima da camada Canal, mas, não cria seu próprio canal [JGroups, 2006].
2.3 Pilha de Protocolo
O JGroups proporciona duas implementações de canal. Um canal baseado no Ensemble e seu proprio canal baseado na pilha de protocolo Java. O segundo é um pilha de protocolos contendo um número de canal em uma lista bidirecional. Todas as mensagens enviadas e recebidas acima do canal devem atravessar a pilha de protocolo. Todas as camadas podem modificar, reordenar, pulas ou acrescentar um cabeçalho em uma mensagem. Uma camada de fragmentação pode quebrar uma mensagens em várias mensagens menores, adicionando um identificador no cabeçalho de cada fragmento, e reagrupar os fragmentos no lado receptor .
A composição da pilha de protocolos, como suas camadas, são determinadas pelo criador do canal: uma string de propriedas define as camadas a ser usadas (e os parâmetros para cada camada). Esta string poderá ser interpretada diferentemente para cada implementação de canal; no JChannel é usada para criar a pilha, dependendo dos nomes dos protocolos definidos na propriedade.
Conhecimento sobre a pilha de protocolo não é necessário quando na aplicação somente usar canais. Porém, quando uma aplicação ignorar as propriedades ja estabelecidas para uma pilha de protocolo, e configurar sua própria pilha, um conhecimento sobre o que cada camada faz individualmente é necessário. Embora sintaticamente é possível empilhar qualquer camada em cima de outra (todas tem a mesma interface), isto não faria sentido semanticamente na maioria dos casos.
2.4 Interfaces
Algumas interfaces mais utilizadas são:
2.4.1 Transport
Esta interface define um pequeno subconjunto de um canais, essencialmente somente os métodos de envio e recebimento de mensagens. Existem várias classes que implementam a Transporte, entre ela a classe Canal. Muitos blocos de construção requerem apenas uma facilidade de bare-bone para o envio e recebimento de mensagens; então a interface de Transporte é criada. Esta interface aumenta a generalização e a portabilidade dos blocos de construção: simplesmente, a interface de Transporte pode ser facilmente apontada para um conjunto de ferramentas, sem que seja necessário qualquer modificação nos blocos de construção [JGroups, 2006].
2.4.2 MessageListener
Ao contrário do pull-style dos canais, alguns blocos de construção (Ex. PullPushAdapter) proporcionam um modelo de entrega de mensagens parecido com o push-style. Neste caso, a entidade responsável pelo aviso de um recebimento de mensagem precisa enviar um mensagem de retorno sempre que uma mensagem é recebida [JGroups, 2006].
2.4.3 MembershipListener
A interface MembershipListener é parecida com a interface MessageListener. Toda vez que uma nova visão, uma mensagem de suspeita ou um bloqueio de evento é recebido, o método correspondente da classe que implementa MembershipListener é chamado [JGroups, 2006].
2.4.4 ChannelListener
A classe que implementa ChannelListener pode usar os método Channel.setChannelListener() para se registar com o canal e obter as informações sobre as mudanças no canal. Toda vez que um canal é fechado, desconectado ou aberto a chamada de retorno é invocada [JGroups, 2006].
2.4.5 Receiver
A interface Receiver pode ser usada para receber todas as mensagens relevantes e olhar as mudanças no push-style [JGroups, 2006].
2.5 Classes úteis
A classe org.jgroups.util.Util contém um conjunto de classes mais usadas que não podem ser determinadas por outros pacotes em particular [JGroups, 2006].
2.5.1 objectToByteBuffer(), objectFromByteBuffer()
Este primeiro método pega um objeto como parâmetro e serializa o objeto em um buffer (o objeto deve ser serializavél). O vetor de byte é retornado. O método é frequentemente usado para serializar uma mensagem em um buffer. O segundo método retorna a reconstrução do objeto do buffer. Ambos os métodos disparam uma excessão se os objetos não puderem ser serializados ou desserializados [JGroups, 2006].
2.5.2 printMessage()
Imprime uma mensagem passada como parâmetro em formato de leitura. Retorna uma string [JGroups, 2006].
2.5.3 activeThreads()
Retorna uma string com uma lista das threads ativas [JGroups, 2006].
2.5.4 printMembers()
Devolve uma lista com o endereço dos membros [JGroups, 2006].
3 AspectWerkz
AspectWerkz é um framework dinâmico para programação orientado a aspectos em Java. É uma ferramenta leve, de alta performance e que pode auxiliar a intregração de POA em projetos novos ou em projetos já existentes. Qualquer classe em Java pode se tornar um aspecto, ou seja, ela poderá ser configura para ter um comportamento que entrelaça o código de outras classes. Não é necessário criar nenhuma interface e nem estender nenhuma outra classe. Os aspectos podem ser definidos utilizando arquivos XML ou anotações Java [Kohl et al, 2005].
O AspectWerkz trabalha com weaving dinâmico e para isso oferece mecanismos chamados de online e offline [Kohl et al, 2005]. A diferença entre os dois modos é que: No modo offline, as classes sofrem uma duas compilações (fases) antes da sua execução permitindo que os aspectos sejam entrelaçados nas aplicações. A primeira compilação é uma compilação padrão utilizando o compilador padrão Java (javac). Na segunda compilação é executado o compilador do AspectWerkz no modo offline, apontando para as novas classes criadas. O compilador modifica os bytecodes das classes adicionando os advices nos join points corretos. Este processo é parecido aos mecanismos de weaving estático, portanto, existe a necessidade de que todas as classes sejam recompiladas para que os aspectos sejam efetivamente entrelaçados nas classes. No modo online, as classes são modificadas durante sua execução de forma transparente [Kohl et al, 2005]. Para que isso aconteça, o AspectWerkz está preso ao mecanismo de baixo nível de carregamento de classes da JVM, permitindo interceptar todas chamadas de carregamento de classes e transformando os bytecodes em tempo de execução [Kohl et al, 2005]. Para isso, AspectWerkz modifica o mecanismo de carga de classes da JVM utilizando HotSwap, no caso de Java 1.4, ou JVMTI no caso de Java 1. [Kohl et al, 2005].
A ferramenta AspectWerkz foi escolhida para a adaptação dos protocolos de controle de concorrência e o protocolo de replicação de dados de um Middleware [Caproni et al, 2006] (feito em AspectJ), devido ao seu mecanismo weaving dinâmico.
3.1 Aspectos
Qualquer classe Java pode ser tornar um aspecto no AspectWerkz, ou melhor dizendo, pode ser definida para ter um comportamento de cortes transversais se tornando uma unidade modular para crosscutting concerns.
Os aspectos não precisam necessariamente estender nenhuma classe em especial ou implementar uma interface específica, mas podem estender qualquer classe caso seja preciso [AspectWerkz, 2005].
· A única exigência que se pede é que o aspecto não tenha construtor pra tudo [AspectWerkz, 2005], ou tenha apenas um dos construtores definidos abaixo:
· Um construtor padrão sem argumentos: necessário somente se o aspecto tiver outros construtores com entrada de parâmetros [AspectWerkz, 2005].
· Um construtor que tenha uma instancia de org.codehaus.aspectwerkz.AspectContext com único parâmetro: necessário somente aos interessados em recuperar informações sobre o sistema em tempo real, acessar parâmetros definidos em tempo de compilação ou acessar meta dados [AspectWerkz, 2005].
O AspectWerkz tentará acessar primeiramente o segundo tipo de construtor supracitado, caso não encontre este construtor, então o construtor padrão será usado. Ainda assim se não for encontrado um construtor uma exceção será disparada.
Em AspectWerkz os aspectos podem ser definidos em XML ou anotações Java. Para a adaptação do middleware foi utilizado apenas definições em XML.
3.2 Advice
O advice nada mais é do que métodos regulares de uma classe em aspecto. Os métodos precisam ser adequados a uma determinada assinatura exceto args(), this() e/ou target() usados no pointcut onde o advice é ligado [AspectWerkz, 2005].
· Para around advices:
o public Object
o public Object
· Para Before, After, After Finally, After Returning (TIPO) and After Throwing (TIPO) advice:
o public void
o public void
o public void
No AspectWerkz os advices podem ser definidos tanto em XML como em anotações Java. Assim como os aspectos os advices fora todos definidos em XML para a adaptação do middleware.
3.2.1 after finally (after)
Advices after finally são sempre executados, o que significa que eles serão chamados mesmo se uma exceção for disparada ou se um método retornou com sucesso [AspectWerkz, 2005].
3.2.2 after returning [TIPO]
O advice after returning [TIPO] será executado normalmente se o método retornar um valor do tipo especificado na declaração do advice [AspectWerkz, 2005].
Se o tipo de retorno não for especificado o advice executará com o retorno de qualquer tipo [AspectWerkz, 2005].
3.2.3 after throwing [TIPO]
O advice after throwing [TIPO] é executado caso o método dispare uma exceção e esta exceção disparada seja do mesmo tipo especificada na declaração do advice [AspectWerkz, 2005].
Se um tipo de exceção não for especificada o advice executará para todas as invocação que retornarem uma exceção [AspectWerkz, 2005].
3.3 Pointcuts
O pointcut é a construção de cortes nos join points, isto é, seleciona conjuntos de join points que são definidos no fluxo do programa [Lima, 2006].
O AspectWerkz permite composição de pointcuts [AspectWerkz, 2005], o que significa que eu posso compor pointcuts utilizando operadores lógicos.
· ! - Operador lógico NOT
· ou OR - Operador lógico OR
· && ou AND - Operador lógico AND
· Parênteses para agrupar
Usando esses operadores em conjunto com os parenteses é possível formar várias expressões algébricas [AspectWerkz, 2005].
É possível definir um pointcut em um aspecto e depois fazer referencia a este pointcut da definição de um outro aspecto. Isto pode ser muito útil para a criação de bibliotecas de pointcuts que pode ser usada durante o projeto ou durante vários outros projetos [AspectWerkz, 2005].
3.4 Definição XML
O que é importante entender é que a definição por XML e a definição por anotações podem facilmente coexistir. A definição XML pode ser usada como uma recolocação ou à definição de Anotação ou apenas um complemento [Lima, 2006].
A definição XML permite definir expressões de pointcuts em arquivos externos [AspectWerkz, 2005]. E pode ser usado para guardar aspectos que precisam ser ajustados sem passar por mais um fase de desenvolvimento [AspectWerkz, 2005]. A principal desvantagem é que a implementação é separada da definição, o que deixa o código mais difícil de manter e reutilizar [AspectWerkz, 2005].
3.4.1 Definição de Aspectos
A definição de um aspecto em XML é feita utilizando o elemento \textit{aspect}, que possui os seguintes atributos:
· class (obrigatório): especifica um nome qualificado para a implementação da classe aspecto [AspectWerkz, 2005].
· deployment-model: especifica o modelo de \textit{deployment} para o aspecto [AspectWerkz, 2005].
o perJVM: posiciona o aspecto como perJVM.
o perClass: posiciona o aspecto como perClass.
o perInstance: posiciona o aspecto como perInstence.
· name: define um nome opcional, porém único para o aspecto no sistema.
Dentro dos elementos aspects do XML, são definidos pointcuts e advices [AspectWerkz, 2005].
3.4.2 Definição de Pointcuts
Quando definimos um pointcut existem dois atributos que precisam ser especificados [AspectWerkz, 2005].
· name (obrigatório): especifica o nome do pointcut [AspectWerkz, 2005].
· expression (obrigatório): especifica a expressão para o pointcut. É onde se define o join point a ser interceptado.
3.4.3 Definição de Advices
Um advice é definido em XML através do elemento advice [AspectWerkz, 2005]. Para definir um advice há três atributos que precisam ser especificados [AspectWerkz, 2005].
· (Obrigatório): O nome do advice é o nome do método da classe aspecto que implementa o advice, pode conter assinatura ou não. Caso haja assinatura o método definido deve existir e conter a mesma assinatura na hierarquia da classe do aspecto.
· type: define os tipos de advice. Os tipo válidos são [AspectWerkz, 2005]:
o around
o before
o after
o after finally
o after returning
o after throwing
o after returning(..)
o after throwing(..)
· bind-to: liga o advice ao pointcut referenciando o nome do pointcut ou definindo um pointcut anônimo [AspectWerkz, 2005].
3.5 Passagem de parâmetros para o aspect
Você tem a opção de passar os parâmetros para seus aspectos [AspectWerkz, 2005]. Para passar um parâmetro para o aspecto simplesmente se adiciona uma etiqueta param na definição do aspecto.
De dentro de uma classe aspecto use a API AspectContext.getParameter("id") para recuperar o valor do parâmetro como uma string [AspectWerkz, 2005].
3.6 Programação Orientada a Aspectos Dinâmica
O AspectWerkz suporta vários tipos de hot deployment e hot undeployment de aspectos. O AspectWerkz utiliza HotSwap para Java 1.4 ou JVMTI para Java 1.5 para alterar o comportamento da aplicação em tempo de execução. Novos aspectos podem ser adicionados ou aspectos antigos podem ser redefinidos ou removidos com a aplicação em execução.
Todos esses serviços são acessados da classe org.codehaus.aspectwerkz.transform. inlining.deployer.Deployer, que possui um rico conjunto de serviços [AspectWerkz, 2005].
3.6.1 Hot deployment
Hot deployment de aspectos é feito usando os métodos Deployer.deploy(..).
DeploymentHandle deploy(Class aspect)
DeploymentHandle deploy(Class aspect, ClassLoader deployLoader)
DeploymentHandle deploy(Class aspect, DeploymentScope scope, ClassLoader deployLoader)
DeploymentHandle deploy(Class aspect, String xmlDef)
DeploymentHandle deploy(Class aspect, String xmlDef, ClassLoader deployLoader)
DeploymentHandle deploy(Class aspect, String xmlDef, DeploymentScope scope)
DeploymentHandle deploy(Class aspect, String xmlDef, DeploymentScope scope, ClassLoader deployLoader)
3.6.2 Hot undeployment
Hot undeployment de aspectos é feito usando os métodos Deployer.undeploy(..).
void undeploy(Class aspect)
void undeploy(Class aspect, ClassLoader loader)
void undeploy(DeploymentHandle deploymentHandle)
3.6.3 Deployment scopes
O uso do Deployment scope oferece mais segurança ao deployment [AspectWerkz, 2005]. O uso é necessário devido ao fato de que nenhuma JVMs hoje suporta redefinição de schema quando redefinimos uma classe [AspectWerkz, 2005].
Por este motivo deve-se definir um tipo especial de pointcut chamado Deployment scopes que irá preparar a aplicação para receber um aspecto futuramente.
3.6.4 Deployment handles
Todos os métodos deploy(..) retornam um DeploymentHandle que é um handle para um específico evento de deployment. Podemos utilizar este handle para reverter as mudanças feitas pelo deployment [AspectWerkz, 2005].
4 Conclusão
Com utilização de aspectos para modularizar as responsabilidades de controle de concorrência e replicação no Middleware [Caproni , 2006], foi possível a implementação da troca dinâmica dessas responsabilidades utilizando o framework AspectWerkz. Com os aspectos definidos em XML, essas responsabilidades passaram a ser independentes do código funcional da aplicação, possibilitando a troca dos protocolos em tempo de execução.
O Uso da ferramenta JGroups facilitou a comunicação em um ambiente de grupo, devido a sua API simples e facilidade de tratamento de mensagens, pois o programador é fica livre de muitas implementações repetitivas e tediosas.
Referência Bibliográfica
[JGoups, 2006]
BAN, Bela. Reliable Multicasting with the JGroups Toolkit, 2006: Disponível em
[AspectWerkz, 2005]
BONÉR, Jonas; VASSEUR, Alexandre. AspectWerkz – Plain Java AOP, 2002-2005: Disponível em:
[Gamma et al, 1995]
GAMMA, Erich; HELM, Richard; JOHSON, Ralph and VLISSIDES, John. Padrões de Projeto: Soluções reutilizáveis de software orientado a objetos. Bookman, 1995.
[Caproni et al, 2006]
CAPRONI, T.T.; GOMES, L.A.F.; FRITZKE, U. Jr.; Middleware Adaptável para Banco de Dados Móveis utilizando Aspectos. III Workshop Brasileiro de Desenvolvimento de Software Orientado a Aspectos (WASP 2006).Florianópolis, Brazil 2006
[Kohl et al, 2005]
KOHL, Karina; WEBER, Taisy; Um Injetor de Falhas de Comunicação construído usando Programação Orientada Aspectos. VI Workshop de Testes e Tolerância a Falhas (WTF 2005). Fortaliza, Brazil 2005.
[Lima, 2006]
LIMA, Denis S.; Adaptação Dinâmica de Controle de Concorrência utilizando Programação Orientada a Aspectos, Poços de Caldas 2006.
