REAJ
Uma ferramenta para engenharia reversa de código AspectJ
Bom, nessa apresentação eu vou falar do REAJ, que é uma ferramenta para engenharia reversa de código-fonte AspectJ.
Sendo que AspectJ é uma extensão da linguagem Java que implementa o paradigma de programação orientada a aspectos. E engenharia reversa, nesse contexto, significa gerar diagramas que representem programas escritos nessa linguagem.
Então eu vou começar falando do paradigma de programação orientada a objetos [aspectos] e engenharia reversa, e depois eu falo mais sobre a ferramenta, a notação que ela usa, sua arquitetura e discutir alguns pontos.
Segundo Dijkstra, a separação de interesses é uma propriedade importante do pensamento científico. Segundo ele, na resolução de um problema, geralmente existem diversos interesses ou preocupações que você tem, e é vantajoso focar em uma de cada vez, ignorando as outras por um tempo.
No contexto do desenvolvimento de software, interesse se traduz em requisito ou funcionalidade do sistema e os sistemas se decompõem naturalmente em módulos, representados aqui por essas caixas. Então é interesse do desenvolvedor separar esses interesses em módulos e implementar esses módulos. E quando você tem esse mapeamento natural entre os interesses, aqui representados pelas nuvens, e os módulos, você tem uma boa decomposição ou modularidade. Isso favorece a compreensão e a evolução do sistema e o reuso de seus módulos em outros sistemas.
Já nesse caso aqui, você tem uma decomposição ruim, porque os interesses... Por exemplo, esse interesse está espalhado entre esses 3 primeiros módulos. Se você olhar esse terceiro módulo, você vai ver também que o código dele é um emaranhado, ele trata de diversos interesses, prejudicando a manutenção, a evolução e o reuso.
Isso acontece porque existem interesses que acabam afetando outros interesses. São os chamados interesses transversais. E a programação orientada a objetos não lida bem com isso, porque ele fornece uma decomposição primária do sistema, privilegiando alguns interesses em detrimento de outros.
A programação orientada a aspectos é um paradigma de programação que permite que os interesses sejam separados em módulos denominados aspectos.
No nível do código-fonte, que é o que o programador olha, você vê que os interesses estão bem modularizados, e aí geralmente no momento da compilação ocorre o que se chama de combinação de aspectos, que é quando o combinador de aspectos pega esse código e mistura as coisas de novo para que possa ser executado pela máquina virtual ou pelo computador.
Os aspectos afetam a estrutura e o comportamento de outros módulos do sistema. Pra isso, eles são compostos de declarações intertipos e adendos. As declarações intertipos afetam a estrutura do sistema. Você tem um aspectos DisplayProfiling, classe Display, interface Profilee. O aspecto pode declarar que a classe Display vai ??? um atributo totalTime, um método getTotalTime() e também fazer com que ele implemente a interface Profilee. Depois da combinação esse é o resultado. A estrutura da classe é modificada.
Já pra falar de alterações comportamentais, eu preciso falar de pontos de junção, que são pontos bem definidos na execução do programa. Aqui nesse exemplo, você tem um código-fonte e vários pontos de junção. Primeiro, a execução do metodo1, e aí ele chama o metodo2, tem a execução do metodo2, aí o metodo2 faz diversas coisas, acaba sua execução e aí volta para o metodo1, que altera a variável x. Todos esses aqui são pontos de junção.
E os pointcuts são expressões que selecionam pontos de junção de acordo com certas propriedades. Aqui você tem o exemplo do pointcut pc1, que seleciona todos os pontos de junção que sejam execuções de métodos começados com "me" ou alterações da variável x. Os pontos de junção selecionados são esses aqui em verde. E aí você tem os adendos, que são porções de código associadas a pointcuts, que executam cada vez que um ponto de junção selecionado é executado. Aqui eu tô ilustrando dois tipos básicos de adendos: adendo before, que executa antes de cada ponto de junção selecionado, e adendo after, que é executado depois.
Isso conclui o que eu queria falar rapidamente sobre programação orientada a aspectos, e agora a gente pode entender melhor a motivação deste trabalho. A programação orientada a aspectos é recente, tem poucas ferramentas ainda, se comparada a, por exemplo, programação orientada a objetos. A linguagem AspectJ, por exemplo, é de 1997. AspectJ, no nível de implementação, é um padrão dominante, é aquilo que é a referência de linguagem de programação com aspectos. Mas em outras atividades, como modelagem, ainda não há um consenso. Pra esse trabalho, eu estudei a abordagem de modelagem aSideML, de Christina, e a AODM, de Stein. O que você tem hoje são sistemas orientados com aspectos, e quando existe a necessidade de fazer manutenção nesses sistemas, o que é sempre um processo caro, faltam ferramentas para que os programadores possam entender mais facilmente esses programas. Ao mesmo tempo, faltam notações pra que você possa comunicar soluções orientadas a aspectos sem ter que ficar usando código-fonte. Existem aquelas, mas não há um consenso e há uma carência de ferramentas. Então o que o REAJ faz... ele visualiza sistemas orientados a aspectos.
No desenvolvimento de software existem várias atividades, e cada uma delas gera uma representação parcial do sistema. Documento de requisitos, modelo de projeto, código-fonte, código executável... E o código executável é a única representação que é usada pelo computador, só que é uma representação muito difícil de entender. Então o código-fonte é a representação de mais baixo nível que ainda pode ser entendida pelas pessoas. E o código-fonte é transformado em código executável através do processo automático de compilação. Com isso, o código-fonte é a representação mais confiável do sistema, porque ela é completa, formal e está sempre atualizada com o código executável. Já essas outras representações, elas podem ser consideradas documentação do sistema porque são representações intuitivas, que ajudam as pessoas a entender o que o sistema faz, mas acabam sendo representações parciais, às vezes ambíguas, e muitas vezes desatualizadas, que não refletem as alterações feitas no código-fonte. A engenharia reversa é o processo de análise do código-fonte, aliás, análise do sistema, pra gerar representações dele em um nível de abstração mais elevado. É o que muitos editores de UML fazem, eles pegam código-fonte numa linguagem orientada a objetos e transformam em um diagrama de classes, por exemplo, e é o que o REAJ faz para programas escritos em AspectJ.
Agora eu vou falar do REAJ. O REAJ, além de ser ??? ferramenta, ele utiliza uma notação visual própria e uma linguagem de modelagem, que é a ReajML, que é uma linguagem usada para descrever programas orientados a aspectos sem tanto detalhe quanto o código-fonte.
Na notação visual, você tem dois tipos de diagramas: o diagrama estrutural, baseado no diagrama de classes da UML, e o diagrama comportamentamental, baseado no diagrama de seqüência. Cada um deles tem uma versão combinada e outra não combinada. E o que há em comum entre eles é que eles evidenciam a relação entre os aspectos e o sistema, pra melhorar a compreensão do sistema.
Vocês tem aí um esboço do diagrama estrutural não combinado, que é um diagrama adequado pra você estudar um aspecto e ver como o aspecto afeta o sistema. Aqui vocês têm a representação do aspecto DisplayProfiling, tem aqui os membros locais, os pointcuts e os adendos. E aí você tem essas caixas ligadas a eles: introduction e pointcut. Cada classe afetada por esse aspecto está dentro de uma caixa introduction, onde é mostrada exatamente quais são as alterações estruturais feitas pelo aspecto. Aqui você vê que a classe Display ganha um método getTotalTime() e passa a implementar a interface Profilee. Aqui tem os pointcuts. Eles mostram quais são os métodos que contêm pontos de junção selecionados por aquele pointcut. Então vocês vêm que esses métodos estão sendo afetados de alguma forma por esse pointcut, pelos adendos que usam esse pointcut.
Eu queria mostrar... o diagrama desse que o REAJ gerou automaticamente a partir de um sistema.
Não quero entrar em muitos detalhes aqui, mas o que eu quero mostrar é que nesse diagrama você acaba tendo muitas caixas e muitas setas, então ele fica confuso pra representar um sistema inteiro ou um pacote de um sistema. O ideal é você escolher um aspecto e representar aquele aspecto em um diagrama, ou realmente pegar poucos elementos.
Esse aqui é o diagrama estrutural combinado, ele é bom pra você estudar uma classe e como os aspectos do sistema afetam essa classe. Cada aspecto aqui é identificado com uma cor e aí você tem os losangos coloridos representando declarações intertipos, então você vê aqui que membros, no caso que métodos foram introduzidos por aspectos e por qual aspecto... essa relação de implementação de interface também foi inserida pelo aspecto DisplayProfiling. E aí você tem os adendos, que são círculos coloridos, que também identificam o aspecto de onde o adendo vem.
Eu quero mostrar mais um exemplo aqui que foi gerado pela ferramenta, automaticamente. Isso é um sistema de ilustração, um editor gráfico, que trabalha com formas geométricas. E aí você tem a seguinte situação: quando uma forma geométrica altera seu estado interno, seja sua posição, tamanho ou qualquer coisa, a tela precisa ser notificada pra que esse objeto seja redesenhado: é o padrão de projeto Observer. Esse padrão de projeto é implementado por esse aspecto DisplayUpdating. Ele introduz nas formas geométricas os métodos addObserver() e notifyObserver(). E além disso, o que é interessante, se você for ver as formas geométricas, quadrado e círculo, você tem esses círculos coloridos identificando os adendos. Esse métodos destacados pelos círculos são exatamente os métodos que alteram o estado interno das formas geométricas. Então você vê com facilidade esse aspecto do sistema, como ele está localizado em seus módulos.
Os diagramas estruturais mostram quais são os métodos afetados por adendos, mas você não sabe dentro daquele método, que ponto exatamente está sendo afetado. Isso é o que mostram os diagramas comportamentais. Você tem a execução toda de um método, e aí você tem essas caixas indicando execução de método e as setas indicando chamadas, como na UML. Essas coisas são pontos de junção, e quando eles são afetados, você desenha eles com a linha mais grossa. E aí você pode colocar esses círculos que representam os adendos que afetam aquele ponto em particular. O bom também é que você pode ver pela posição do círculo, com facilidade, que tipo de adendo ele está representando. Esses aqui que aparecem em cima, 1, 2, e 3, são adendos before e os que aparecem embaixo são adendos after. E a cor é relacionada ao aspecto. Você tem ali as descrições dos adendos na linguagem AspectJ.
No diagrama comportamental combinado, esses círculos são substituídos por chamadas aos adendos. Os aspectos aparecem lá em cima, Aspect1 e Aspect2. Você pode, opcionalmente, usar uma moldura, rotulada JP, e você separa um ponto de junção e todos os adendos associados a ele, pra dar maior clareza a essa apresentação.
Falando agora da ferramenta, o REAJ é dividido em duas partes: tem o ReajCompiler, que pega código-fonte na linguagem AspectJ e transforma isso em um modelo na linguagem ReajML, e o ReajViewer, que pega esse modelo e gera diagramas. O ReajCompiler é como um compilador tradicional de uma linguagem, tem todas aquelas etapas de análise léxica, sintática, semântica e além disso ele calcula quais são os pontos de junção selecionados por cada pointcut. Com isso tudo, ele tem um custo muito elevado computacionalmente. O interessante é que todas essas coisas que ele calcula ele deixa gravadas no modelo ReajML, o ReajML já tem os pointcuts calculados. O ReajViewer é uma coisa mais simples, já pega as informações calculadas e só faz a transformação pra imagem. A vantagem de ter um modelo ReajML, uma representação intermediária, é que você pode pegar o código e compilar uma vez só, que é um processo custo, e depois usar o ReajViewer várias vezes. Outra coisa é que essa linguagem ReajML é bastante simples, na verdade os modelos descritos com ela são documentos XML, e existem diversas bibliotecas disponíveis para fazer análise de XML. Então é relativamente fácil você fazer uma ferramenta que trabalhe com esse modelo. Então você poderia, no lugar do ReajViewer, ter uma outra ferramenta ou mesmo um outro visualizador feito por terceiros. E aqui tem um detalhe do ReajViewer, como ele está implementado atualmente. Ele pega um modelo, e usa duas ferramentas: o Graphviz pra gerar os diagramas estruturais e o SEQUENCE pra gerar os diagramas comportamentais. Ele usa representações desses programas, são representações textuais usado pra gerar esssa imagens.
Aqui outra visão da arquitetura, em um nível geral. Aí você tem a divisão ReajViewer e ReajCompiler. O ReajCompiler, ele próprio é dividido em duas partes: a retaguarda e a vanguarda. A vanguarda é aquela parte que faz análise do código-fonte e computa os pointcuts e ele usa uma API, que é a retaguarda, pra gerar um modelo ReajML, que realmente pega essas coisas e coloca em um arquivo XML. A retaguarda usa essa biblioteca JDOM, que é uma biblioteca para análise e síntese de documentos XML. Já a vanguarda é uma extensão do compilador abc, ou AspectBench Compiler. O abc é um compilador da linguagem AspectJ, que não é o compilador oficial, mas ele foi projetado pra ser extensível, pra ser relativamente fácil você implementar extensões da linguagem AspectJ e ter um compilador funcional pra aquela sua extensão. A vantagem de você ter essa separação entre vanguarda e retaguarda é que se você quer usar o REAJ pra fazer visualização de código orientado a aspectos em outra linguagem que não AspectJ, você só precisa reimplementar a vanguarda, para a linguagem de sua preferência.
Dessas coisas todas, a maioria que eu falei foi implementada com exceção dos diagramas comportamentais. Eu usei essa ferramenta SEQUENCE mas, da maneira como eu concebi eu precisaria ter uns círculos no meu diagrama e linhas mais grossas, então eu precisaria modificar o SEQUENCE, coisa que eu não fiz. Então, hoje, a geração de diagramas comportamentais ainda não está completa, não usa toda aquela especificação. Você tem a seqüência, mas não mostra todos aqueles detalhes. Outra coisa é que a interação com o usuário foi implementada pela linha de comando. Eu julguei que seria mais vantajoso trabalhar na coisa em si e não fiz uma interface gráfica. Com isso, acaba sendo um pouco difícil usar a ferramenta quando você quer coisas interativas, como selecionar quais são os elementos que você quer que apareçam no diagrama. Se você tivesse uma interface gráfica, teriam possibilidades de navegação interessantes, como você estar em um diagrama estrutural, vendo um método sendo afetado por adendos, e você clica nele e aparece um diagrama de seqüência, um diagrama comportamental. Mas isso é trabalho futuro. Outra coisa que vale a pena destacar é que os diagramas mostram a extensão dos pointcuts, ou seja, quais são os pontos de junção selecionados pelos pointcuts. Eles não fornecem realmente uma representação da expressão de pointcut. Então, com isso, fica difícil entender, vendo um diagrama desses, como um aspecto afetaria um outro sistema, porque você não conhece a expressão. Um trabalho futuro também interessante seria fazer a visualização de linguagens que sejam extensões de AspectJ implementadas com o abc. E aí vem a vantagem de ter usado o abc e não o compilador oficial de AspectJ. E outra coisa, finalmente, é que eu tô falando que o REAJ melhora a compreensão, a comunicação, mas eu não validei isso experimentalmente. Seria uma coisa importante fazer essa validação e também comparar o REAJ com outras ferramentas disponíveis com esse mesmo propósito de visualização. Pesquisando, eu só achei um experimento desse tipo, que tá vinculado ao desenvolvedor de uma das ferramentas, e a impressão que dá é que foi um experimento tendencioso, acaba favorecendo muito a própria ferramenta dele, então não existe ainda um experimento sério pra avaliar essas coisas.
E aí é isso... Eu criei essa página no TWiki que tem a ferramenta pra download, instruções e outros exemplos.