4.4 Implementação
4.4.3 Processo de compilação
O processo de compilação é composto por sete passos que vai da leitura das anotações AJCSP nas classes, até a geração dos bytecodes, como mostra a Figura 4.5. Abaixo, cada passo da Figura4.5é detalhado para melhor entendimento:
a) Análise sintática: uma árvore sintática é gerada a partir das anotações AJCSP nas
classes. É utilizado o JavaCC (java.net,2007) para fazer a análise sintática confron- tando com uma BNF conhecida (Figura4.3), e assim gerando uma estrutura com todos os operadores AJCSP encontrados.
b) Análise Semântica: neste passo são verificados se existem inconsistências nos even-
tos, canais, métodos e processos que foram criados previamente. Por exemplo, algum canal não declarado, canais com nomes iguais, processos não especificados etc.
c) Geração de código: é realizada a geração de código JCSP a partir da tradução da
especificação AJCSP encontrado nas anotações. Esse procedimento de geração de código segue as regras de tradução apresentadas mais adiante.
d) Geração de aspectos: entidades AspectJ são geradas. Cada classe com especifica-
ções AJCSP, terá um aspecto associado que contém código JCSP traduzido das anotações AJCSP.
e) Weaving: o compilador do AspectJ é executado com o objetivo de iniciar o processo
de weaving entre as classes do sistema com os aspectos contendo código concor- rente.
f) Geração dos byecodes: geração dos bytecodes resultado da mistura entre o código
sequencial original e código concorrente JCSP.
A Figura 4.7 mostra um exemplo do código gerado pelo compilador AJCSP. Este aspecto utiliza o mecanismo de static crosscutting (linhas 3 e 5) para inserir o método run()dentro da classe Clock. O método run() implementa o código concorrente em JCSP traduzido das anotações AJCSP. Observa-se também que o aspecto contém uma referência para a classe Environment. Esta classe pertence a API AJCSP e é responsável por coordenar a comunicação entre os processos. Ela contém uma coleção com todos os canais declarados no programa e através do método getChannel pas- sando o nome do canal como argumento, é possível ter uma referência de qualquer canal declarado.
4.5. TRADUÇÃO
1 //@# String varA; 2 //@# String varB;
3 //@# ch1?varA -> varB = toLowerCase(varA) -> ch2!varB -> Skip() 4 class Classe {
5 private String toLowerCase(String param){ ...}
6 }
Figura 4.6 Utilizando métodos em AJCSP.
1 p u b l i c a s p e c t C l o c k _ A s p e c t { 2 d e c l a r e p a r e n t s : C l o c k i m p l e m e n t s C S P r o c e s s ; 3 p r i v a t e E n v i r o n m e n t P r o d u c e r . env = E n v i r o n m e n t . g e t E n v ( ) ; 4 5 p u b l i c v o i d C l o c k . r u n ( ) { 6 w h i l e ( t r u e ) { 7 env . g e t C h a n n e l ( " t i c k " ) . g e t I n ( ) . r e a d ( ) ; 8 s l e e p ( ) ; 9 } 10 } 11 }
Figura 4.7 Código AspectJ gerado com comportamento concorrente.
A junção do código concorrente gerado com todo o código sequencial original acon- tece quando o compilador do AspectJ (ajc) é executado, no passo g) da Figura4.5. Esse procedimento também conhecido como Weaving (Laddad,2003), gera o bytecode final juntando as duas partes, como pode ser visto na Figura 4.8. Portanto, o programa final terá referências a API JCSP e AJCSP, no entanto, o código fonte continua limpo, sem dependências, apenas com anotações AJCSP.
4.5
Tradução
A tradução de CSP para JCSP é quase direta, todos os operadores encontrados em CSP podem ser utilizados em Java, utilizando a API JCSP. Algumas construções não têm uma tradução direta, por exemplo, a chamada recursiva, que neste caso é traduzida para uma execução em laço utilizando while(true).
Alguns trabalhos como (Oliveira and Cavalcanti, 2004) e (Freitas and Cavalcanti,
2006) criaram uma estratégia de tradução de Circus para JCSP, que serviram como base para a criação da tradução de AJCSP para JCSP.
4.5.1
Classes Java
No processo de compilação, todas anotações especificadas nas classes são traduzidas para código JCSP que implementam o comportamento. Este código JCSP é injetado nas classes originais através de aspectos utilizando inter-types. O exemplo abaixo, mostra a
4.5. TRADUÇÃO
Figura 4.8 Junção do aspecto gerado com o código original.
tradução de uma classe Java que contém anotações AJCSP. Classe Teste utlizando anotações AJCSP.
/ /@# <C o m p o r t a m e n t o c o n c o r r e n t e em JCSP>
p u b l i c c l a s s T e s t e { . . .
}
Aspecto gerado pelo processo de compilação AJCSP. p u b l i c a s p e c t T e s t e _ A s p e c t { d e c l a r e p a r e n t s : T e s t e i m p l e m e n t s C S P r o c e s s ; p r i v a t e E n v i r o n m e n t P r o d u c e r . env = E n v i r o n m e n t . g e t E n v ( ) ; p u b l i c v o i d P r o d u c e r . r u n ( ) { < C o m p o r t a m e n t o c o n c o r r e n t e em JCSP > } }
Nota-se que o aspecto gerado introduz herança à interface CSProcess da API JCSP que, como visto anteriormente, possibilita transformar uma classe java em um processo. A implementação do método run() é impressindível porque é nele que será especifi- cado todo o comportamento concorrente. Ou seja, as traduções das anotações AJCSP são trazidas para dentro do método run().
Outro ponto importante é a inserção da variável env do tipo Environment. Esta classe faz parte da API AJCSP e é responsável por fazer todo o controle das sincroniza- ções dos canais. Através dela qualquer processo pode adquirir a referência de um canal específico passando como argumento apenas o nome do mesmo.
Apesar de todos os aspectos introduzirem uma instância de Environment nas classes do projeto, ela segue o padrão de projeto singleton. Consequentemente, todas as classes recebem uma referência para o mesmo objeto.
4.5. TRADUÇÃO
4.5.2
Processos
A partir desta seção, será apresentado as anotações suportados pelo o AJCSP e suas respectivas traduções.
No comportamento AJCSP, processos são chamados frequentemente para terem seus comportamentos executados. Esses processos podem estar presentes no projeto ou po- dem ser processos pré-definidos. É o caso dos processos SKIP e STOP, que como visto na Seção 2, representam a finalização com sucesso de um sistema e deadlock respectiva- mente. A API JCSP representa o processo SKIP através da classe Skip, e STOP pela classe Stop. Processos em AJCSP. P r o c e s s o Q u a l q u e r ( ) SKIP ( ) STOP ( ) Tradução para JCSP. ( new o r g . j c s p . l a n g . P r o c e s s o Q u a l q u e r ( ) ) . r u n ( ) ; ( new o r g . j c s p . l a n g . S k i p ( ) ) . r u n ( ) ; ( new o r g . j c s p . l a n g . S t o p ( ) ) . r u n ( ) ;
Visto que, em JCSP um processo é uma classe que implementa CSProcess, a execução deste processo é definido pela execução do método run().
4.5.3
Prefixo
O operador de prefixo é representado por uma sincronização de evento seguido da exe- cução de um processo específico. A sincronização do evento pode ser caracterizada pela leitura/escrita de um canal ou pela transição de estado em outro processo pelo mesmo evento. Como pode ser visto na tradução a seguir, o evento α representa um evento de sincronização.
Através da referência de Environment introduzida pelo aspecto, é possível ter acesso a todos os eventos declarados nas anotações. O método getChannel(String) da classe Environment retorna uma referência ao canal do qual o nome é passado como argumento.
Prefixo. α → P()
4.5. TRADUÇÃO
env . g e t C h a n n e l (α) . i n ( ) . r e a d ( ) ; ( new P ( ) ) . r u n ( ) ;
A sincronização acontece quando dois processos alcançam um evento específico prosseguindo com suas execuções. Porém, em JCSP, ela é tratada como uma comu- nicação entre processos sem o envio de nenhuma informação relevante, apenas para fins de sincronização entre os canais, ou seja, um escreve e outro lê.
Portanto para garantir que dois processos distintos podem sincronizar em um evento, o AJCSP utiliza a regra acima, para traduzir um evento de sincronização em uma cha- mada ao método read() ou o método write() do canal, e assim garantir que sempre existirá um processo lendo e outro escrevendo no canal garantindo o sincronismo entre os processos.
Desta forma, o método read() inicia o processo de sincronização esperando que outro evento escreva algo, utilizando o método write(Object), prosseguindo com o fluxo até atingir o processo P. Apesar da tradução mostrar fixo o uso do método read() na tradução da sincronização de eventos, o compilador controla a geração de código utilizando os métodos read() (leitura) ou write() (escrita) garantindo que sempre haverá uma leitura e uma escrita no evento utilizado.
4.5.4
Leitura de canais
A leitura de um canal é bem semelhante ao prefixo. A diferença acontece no armaze- namento do valor lido pelo método read(), que, neste caso, é armazenado em uma variável que precisa ser declarada nas anotações AJCSP antes de utilizá-la.
Leitura de canal. µ x ; α?x → P() Tradução. µ x ; x = (µ) env . g e t C h a n n e l (α) . i n ( ) . r e a d ( ) ; ( new P ( ) ) . r u n ( ) ;
Como o valor esperado é do tipo µ, é necessário fazer casting. Porque o método read()retorna um objeto do tipo Object. A variável declarada pode ser de qualquer tipo da linguagem java, ou seja, tipos primitivos e o tipo referência.
4.5. TRADUÇÃO
4.5.5
Escrita de canais
A escrita de um valor em um canal segue a mesma implementação da leitura. A única diferença é a chamada ao método write(Object) passando o valor a ser escrito. Escrevendo no canal. µ x = . . . ; α!x → P() Tradução. µ x = . . . ; env . g e t C h a n n e l (α) . o u t ( ) . w r i t e ( x ) ; ( new P ( ) ) . r u n ( ) ;
A API JCSP possibilita a escrita de qualquer tipo no canal de comunicação, pois o método write() recebe Object como argumento.
4.5.6
Utilizando métodos
Como mencionado anteriormente, AJCSP possibilita a utilização de métodos no opera- dor de prefixo, assim criando uma interação entre a especificação concorrente e o código de regra de negócio. O valor de retorno de um método pode ser escrito em um canal ou receber um valor lido de um canal como argumento. Considerando o método β() que recebe uma variável do tipoµ como argumento:
Do canal para o método. µ x ; α?x → β(x) → P() Tradução. µ x ; x = (µ) env . g e t C h a n n e l (α) . i n ( ) . r e a d ( ) ; β(x) ( new P ( ) ) . r u n ( ) ;
Os tipos das variáveis lidas e passadas para os métodos como argumento são ve- rificadas em tempo de execução, podendo gerar alguma exceção caso os tipos sejam incompatíveis. Apenas métodos declarados na própria classe estão aptos a interagir com as anotações AJCSP.
Canais também podem escrever valores gerados por métodos. Considerando o mé- todoσ que retornaµ:
4.5. TRADUÇÃO
Do método para o canal. µ x ; x = σ() → α!x → P() Tradução. µ x = σ() ; env . g e t C h a n n e l (α) . i n ( ) . r e a d ( ) ; ( new P ( ) ) . r u n ( ) ;
4.5.7
Escolha determinística
O operador de escolha utiliza as regras impostas pelo operador de prefixo. Em outras palavras, ele possibilita escolher dois caminhos distintos, dependendo de qual evento ocorreu a sincronização.
A tradução exibida abaixo, mostra que o fluxo pode se comportar como o processo P ou Q, mas para isso um dos eventos α ou ν precisa estar sincronizado com outro processo.
Escolha externa.
α −> P [ ∼ ] ν −> Q
Tradução.
Guard [ ] g u a r d s A r r a y = new Guard [ ] {
( Guard ) env . g e t C h a n n e l (α) . g e t I n ( ) , ( Guard ) env . g e t C h a n n e l (ν) . g e t I n ( ) } ; A l t e r n a t i v e a l t = new A l t e r n a t i v e ( g u a r d s A r r a y ) ; s w i t c h ( a l t . s e l e c t ( ) ) { c a s e 0 : env . g e t C h a n n e l (α) . g e t I n ( ) . r e a d ( ) ; ( new P ( ) ) . r u n ( ) ; break; c a s e 1 : env . g e t C h a n n e l (ν) . g e t I n ( ) . r e a d ( ) ; ( new Q ( ) ) . r u n ( ) ; break; }
A tradução consiste em criar um array de eventos representado pela classe Guard da biblioteca JCSP, que armazenará as referências dos canais de entradaα eν. A coleção é passada então como argumento para o construtor da classe Alternative, que através do método select() retorna a posição do evento sincronizado armazenado no array guardsArray. Por exemplo, o sincronismo do evento α com outro processo indica
4.5. TRADUÇÃO
que o switch reberá o valor 0, já que o eventoα está na posição 0 da coleção de guardas. Portanto, o bloco de código dentro do case 0 será executado.
Operador de escolha externa adotado pelo AJCSP é representado pelo [∼], diferen- temente de [] utilizado na linguagem CSP especificada por (Hoare,1985).