identificaÇÃo de violaÇÕes de arquitetura num cenÁrio de...
TRANSCRIPT
IDENTIFICAÇÃO DE VIOLAÇÕES DE ARQUITETURA NUM CENÁRIO DE
DESENVOLVIMENTO ORIENTADO A MODELO
Marcio Antelio Neves da Silva
Dissertação de mestrado apresentada ao
Programa de Pós-graduação em Engenharia de
Sistemas e Computação, COPPE, da
Universidade Federal do Rio de Janeiro, como
parte dos requisitos necessários à obtenção do
título de Mestre em Engenharia de Sistemas e
Computação.
Orientadores: Jano Moreira de Souza
Rodrigo Salvador Monteiro
Rio de Janeiro
Julho de 2011
IDENTIFICAÇÃO DE VIOLAÇÕES DE ARQUITETURA NUM CENÁRIO DE
DESENVOLVIMENTO ORIENTADO A MODELO
Marcio Antelio Neves da Silva
DISSERTAÇÃO SUBMETIDA AO CORPO DOCENTE DO INSTITUTO ALBERTO
LUIZ COIMBRA DE PÓS-GRADUAÇÃO E PESQUISA DE ENGENHARIA
(COPPE) DA UNIVERSIDADE FEDERAL DO RIO DE JANEIRO COMO PARTE
DOS REQUISITOS NECESSÁRIOS PARA A OBTENÇÃO DO GRAU DE MESTRE
EM CIÊNCIAS EM ENGENHARIA DE SISTEMAS E COMPUTAÇÃO.
Examinada por:
________________________________________________ Prof. Jano Moreira de Souza, Ph. D.
________________________________________________ Prof. Rodrigo Salvador Monteiro, D. Sc.
________________________________________________ Prof. Geraldo Zimbrão da Silva, D. Sc.
________________________________________________ Prof. Leonardo Guerreiro Azevedo, D. Sc.
RIO DE JANEIRO, RJ – BRASIL
JULHO DE 2011
iii
Silva, Marcio Antelio Neves da
Identificação de violações de arquitetura num cenário de
desenvolvimento orientado a modelo / Marcio Antelio Neves da
Silva. – Rio de Janeiro: UFRJ/COPPE, 2011.
X, 118 p.: il.; 29,7 cm.
Orientadores: Jano Moreira de Souza
Rodrigo Salvador Monteiro
Dissertação (mestrado) – UFRJ / COPPE / Programa de
Engenharia de Sistemas e Computação, 2011.
Referências bibliográficas: p. 87 - 98
1. Violações de arquitetura 2. Arquitetura orientada a
modelo I. Souza, Jano Moreira de et al. II. Universidade
Federal do Rio de Janeiro, COPPE, Programa de Engenharia de
Sistemas e Computação. III. Título
iv
DEDICATÓRIA
À minha futura esposa Ana Luiza por sempre estar do meu lado.
À minha família pelo apoio.
v
AGRADECIMENTOS
Agradeço ao meu orientador Jano pelo conhecimento compartilhado em cada
reunião de andamento da tese, em cada disciplina do mestrado, levando a construção
deste trabalho.
Agradeço ao meu orientador Rodrigo Salvador por sempre estar ao meu lado
durante minha trajetória acadêmica e profissional. Muito obrigado por compartilhar sua
experiência desde os tempos de estágio, por ter me ajudado com o projeto final e hoje
por orientar a construção deste árduo trabalho. Saiba que como pessoa e profissional,
sempre será uma referência para mim, exceto pelo fato de ser vascaíno.
Agradeço ao professor Zimbrão e ao professor Leonardo por aceitarem estar na
banca e contribuírem com este trabalho.
Agradeço a minha futura esposa Ana Luiza, por sempre estar do meu lado, me
apoiando para que eu realize meus e nossos sonhos, independente das dificuldades no
caminho. Eternamente grato pelo fato de lá no início do mestrado, nunca permitir que
eu ficasse enrolando com a escrita da tese. Só tinha de ser com você!!!.
Agradeço aos meus pais Antelio e Marise por me ensinarem os valores da
educação e da ética, ajudando a construir o caráter que tenho hoje. Agradeço ao meu
irmão Marco Antelio por ter acreditado no meu potencial e ter financiado meus estudos
para que eu chegasse até aqui. Também agradeço a minha irmã Darlise pelo seu
exemplo de vida e ao meu sobrinho Michel pelas inúmeras partidas de FIFA para
relaxar, afinal jogar ajuda a estimular o raciocínio para construir esta tese.
Agradeço ao CAPES por apoiar este trabalho.
Chego no fim desse ciclo com o objetivo de também ajudar a compartilhar
conhecimento, única riqueza que uma vez conquistada, nunca poderá ser retirada. Para
isso, seguirei a carreira de professor e iniciar um ciclo mais desafiador ainda.
vi
Resumo da Dissertação apresentada à COPPE/UFRJ como parte dos requisitos
necessários para a obtenção do grau de Mestre em Ciências (M.Sc.)
IDENTIFICAÇÃO DE VIOLAÇÕES DE ARQUITETURA NUM CENÁRIO DE
DESENVOLVIMENTO ORIENTADO A MODELO
Marcio Antelio Neves da Silva
Julho / 2011
Orientadores: Jano Moreira de Souza
Rodrigo Salvador Monteiro
Programa: Engenharia de Sistemas e Computação
A arquitetura de uma aplicação consiste na definição de componentes e nas relações
de dependência permitidas entre os mesmos, as quais chamamos de regras arquiteturais.
Em uma abordagem MDA, as transformações de modelo fornecem o mapeamento entre
os artefatos e os componentes da arquitetura. A partir da análise do código fonte e da
aplicação destas regras, o arquiteto pode identificar as relações de dependência não
permitidas, definidas como violações de arquitetura. A proposta desta tese é automatizar
a identificação de tais violações nos cenários de desenvolvimento orientado a modelo
com geração completa ou parcial de código. Na abordagem de geração completa, a
proposta permite identificar violações oriundas de evoluções das transformações de
modelo. Na abordagem de geração parcial, além de monitorar violações originadas em
evoluções de transformações, a proposta também permite validar se o código inserido
em pontos de implementação obedecem as regras arquiteturais pré-definidas.
vii
Abstract of Dissertation presented to COPPE /UFRJ as a partial fulfillment of the
requirements for the degree of Master of Science (M.Sc.)
ARCHITECTURAL BREACHES DETECTION IN A MODEL DRIVEN
DEVELOPMENT SCENARIO
Marcio Antelio Neves da Silva
July / 2011
Advisors: Jano Moreira de Souza
Rodrigo Salvador Monteiro
Department: Systems and Computer Engineering
The architecture of an application is composed by components and dependency
relations allowed between them, named as architectural rules. In a MDA approach,
model transformations provide the mapping between artifacts and architectural
components. The architect, analyzing source code and these rules, can identify the
dependency relations that are not allowed, defined as architectural breaches. The
proposal of this thesis is to automate the detection of such violations in a model driven
development scenario using complete or partial generation of code. In the full code
generation approach, it can detect violations due to model transformations evolution. In
the partial code generation approach, besides monitoring breaches due to model
transformations evolution, our proposal validates if customizable parts of the source
code via implementation points obey predefined architectural rules.
viii
SUMÁRIO
1. Introdução .............................................................................................................1
2. Fundamentação teórica ..........................................................................................4
2.1 Violações de arquitetura ......................................................................................4
2.2 Geração de código ...............................................................................................6
2.2.1 Abordagem MDA de geração total do código................................................9
2.2.2 Abordagem MDA de geração parcial do código ..........................................11
3. Trabalhos Relacionados.......................................................................................14
3.1 Engenharia reversa ............................................................................................14
3.2 Detecção de violações........................................................................................20
4. A proposta...........................................................................................................24
4.1 Vinculação dos artefatos aos elementos de modelo ............................................27
4.2 Extração de dependências ..................................................................................32
4.3 Especificação da arquitetura ..............................................................................34
5. Implementação ....................................................................................................39
5.1 A construção da Matriz de Rastreabilidade ........................................................41
5.2 A definição dos grupos dos artefatos..................................................................45
5.3 A extração do grafo de dependência...................................................................48
6. Avaliação Experimental e detecção de violações de arquitetura ...............................55
6.1 Visualização do grafo de dependência ...............................................................56
6.2 Aplicando a proposta para a visualização de violações arquiteturais...................68
6.2.1 Criação das regras arquiteturais...................................................................72
6.2.2 Visualização do grafo de violações arquiteturais .........................................77
7. Conclusão............................................................................................................85
Referências Biliográficas.............................................................................................87
Anexos........................................................................................................................99
Anexo 1.................................................................................................................100
Anexo 2.................................................................................................................116
ix
LISTA DE FIGURAS
Figura 1. Arquitetura OSI..............................................................................................6
Figura 2. Abordagem de geração total do código .........................................................10
Figura 3. Abordagem de geração parcial do código .....................................................12
Figura 4. Etapas para aplicação da engenharia reversa em um sistema legado adotando a
abordagem MDA.........................................................................................................16
Figura 5. Processo de engenharia reversa utilizando templates como gramática...........18
Figura 6. Exemplo da abordagem de engenharia reversa utilizando templates..............19
Figura 7. A proposta....................................................................................................25
Figura 8. Aplicando a proposta para a manutenção automática de artefatos do
projeto..............................................................................................................................36
Figura 9. Um elemento de modelo é responsável pela geração de um artefato..............28
Figura 10. Um artefato é gerado a partir de informações descritas em vários elementos
de modelo ...................................................................................................................30
Figura 11. Vários artefatos são gerados a partir de um único elemento de modelo .......31
Figura 12. A geração de um determinado artefato independe dos modelos...................32
Figura 13. Extração e filtragem das dependências........................................................33
Figura 14. Diagrama de componentes..........................................................................37
Figura 15. Visão arquitetural do MDArte para a geração de artefatos a partir do modelo
....................................................................................................................................41
Figura 16. Matriz de Rastreabilidade ...........................................................................45
Figura 17. A definição dos grupos baseado na estrutura dos cartuchos do framework
MDArte.......................................................................................................................46
Figura 18. Classificação dos artefatos..........................................................................50
Figura 19. Estrutura XML representando o grafo de dependência final dos artefatos ...52
Figura 20. Implementação da proposta ........................................................................53
Figura 21. Navegabilidade entre os elementos de modelo ............................................57
x
Figura 22. Navegabilidade a partir de um caso de uso até os elementos de seu diagrama
de atividade.................................................................................................................58
Figura 23. Grafo de dependência dos artefatos e grafo de dependência dos elementos de
modelo correspondentes ..............................................................................................60
Figura 24. Identificação da regra incorreta de transformação do modelo......................64
Figura 25. Regra incorreta de transformação do modelo ..............................................65
Figura 26. Descrição da solução da regra incorreta de transformação do modelo .........66
Figura 27. O caso teste ................................................................................................67
Figura 28. A proposta para visualização de violações de arquitetura ............................69
Figura 29. Conceitos sobre violações arquiteturais ......................................................71
Figura 30. Modelagem das regras arquiteturais entre grupos........................................72
Figura 31. Estrutura XML de definição das regras arquiteturais...................................74
Figura 32. Estrutura gerada a partir da transformação do modelo de regras arquiteturais
do módulo de consulta de países..................................................................................75
Figura 33. Modelagem das regras arquiteturais entre grupos........................................78
Figura 34. Modelagem de uma regra arquitetural específica para um subgrupo............79
Figura 35. Descrição de uma regra arquitetural específica para um subgrupo...............80
Figura 36. Visualização do grafo de dependência ........................................................82
Figura 37. Visualização das violações arquiteturais em um grafo de dependência de
artefatos ......................................................................................................................83
1
1. Introdução
A arquitetura orientada a modelo (da sigla em inglês MDA, Model-Driven
Architecture (MDA, 2010)) é uma abordagem de desenvolvimento que separa a lógica
de negócio da tecnologia que será adotada para a construção do sistema (OMG, 2010).
Ela também apresenta a vantagem de possuir informações síncronas entre a aplicação e
sua documentação. Visando alcançar estes objetivos, mecanismos de padronização de
notações e a interoperabilidade de ferramentas são apresentados em (KLEPPE et al.,
2002). A abordagem MDA pode ser aplicada através de transformações de modelo para
texto, transformações de modelo para modelo e transformações de texto para modelo
como exemplificados respectivamente em (VALDERAS et al., 2005, REUS et al., 2006,
MAZÓN, 2008).
As ferramentas de desenvolvimento orientado a modelo utilizam como entrada
artefatos de projeto, em sua maioria descritos em notação UML (UML, 2010), para
representar casos de uso, componentes de serviço, workflows etc. No entanto, existe a
possibilidade de usar a própria abordagem MDA para gerar novos artefatos de projeto.
Por exemplo, diagramas de dependência de componentes podem ser gerados para
auxiliar analistas na manutenção de sistemas de informação.
De acordo com a nossa experiência em pesquisas nesta área, todas as abordagens
MDA podem pertencer a duas categorias: geração parcial e total do código fonte. A
geração total do código fonte tem como pré-requisito a capacidade de expressar através
de modelos todos os requisitos e funcionalidades a serem atendidos pela aplicação. A
2
abordagem de geração parcial permite que o modelo represente apenas parte do
comportamento da aplicação e que o mesmo seja complementado através de código
escrito diretamente na plataforma alvo.
As informações necessárias para geração de novos artefatos de projeto podem estar
dispersas em modelos e códigos fonte. Nesta tese será descrita a automatização da
construção de um grafo de dependência de componentes a partir de todos os artefatos da
aplicação (modelos e código fonte). Conhecendo a arquitetura da aplicação, ou seja,
seus grupos de componentes e as relações de dependência permitidas entre os mesmos,
podemos também automatizar a identificação de violações, que seriam justamente as
relações não permitidas. Mapeamentos descritos nas transformações de modelos
permitem identificar o grupo de componentes a que cada artefato pertence. A
automatização de todo o processo possibilita um monitoramento contínuo da aplicação
garantindo a aderência à arquitetura estabelecida.
Este trabalho é resultado da nossa experiência com o desenvolvimento de
aplicações reais de grande escala que utilizam o framework MDArte (MDARTE, 2010),
que é baseado no framework AndroMDA (ANDROMDA, 2010). Este framework de
código aberto é composto por cartuchos customizáveis que guiam a geração de código a
partir de modelos que utilizam a notação UML. Baseando–se no conhecimento
adquirido do desenvolvimento e manutenção destes projetos reais, foi observada a
extrema importância de possuir artefatos atualizados sobre toda a aplicação durante seu
ciclo de vida. Esta tese está sendo aplicada no desenvolvimento de novas
funcionalidades para o framework MDArte com objetivo de disponibilizá-las para
projetos futuros.
Este trabalho encontra-se organizado em sete capítulos. A discussão entre as
abordagens de geração parcial e total de código, além da finalidade de detecção de
3
violações de arquitetura em ambas as abordagens é debatida no capítulo 2. O capítulo 3
aborda os trabalhos relacionados. No Capítulo 4 a proposta deste trabalho é discutida.
Baseando-se nesta proposta, a implementação de todo o processo é descrito no capítulo
5. A visualização das violações de arquitetura é mostrada no capítulo 6. No capítulo 7 ,
são apresentadas as conclusões e sugestões para trabalhos futuros.
4
2. Fundamentação teórica
Neste capítulo primeiramente serão apresentadas as definições de arquitetura de um
software e suas violações. Posteriormente serão apresentados dois tipos de abordagem
de geração de código: geração de código parcial e de código total a partir de modelos.
2.1 Violações de arquitetura
GUTTAG et al. (1985) apresenta o conceito do termo arquitetura de software a
partir da visão de um arquiteto e de pessoas leigas em relação a este assunto. Estas
geralmente associam arquitetura como uma estrutura física composta por partes que se
relacionam. Diferentemente da visão anterior, na perspectiva abordada por um arquiteto
é necessário construir uma arquitetura que satisfaça os clientes envolvidos, que
apresente segurança, acesso, manutenção, comunicação etc.
O termo arquitetura de software tem apresentado inúmeras definições. Ele tem sido
definido como uma estrutura composta por componentes, e regras caracterizando as
interações destes componentes (JONES, 1994). Este termo também tem sido discutido
por PERRY e WOLF (1992) como elementos e suas interações. GARLAN e SHAW
(1992) os definem como componentes, conectores e configurações. Em relação aos
sistemas desenvolvidos em componentes (GARLAN, SHAW, 1992, PERRY, WOLF
1992), a organização da arquitetura da aplicação como um todo apresenta um novo
conjunto de problemas de projeto. Uma das observações relativas à arquitetura
identificada por GARLAN e PERRY (1994) são as descrições em linguagens de alto
nível de sistemas baseados em grafos de interação de componentes. Eles identificam
5
componentes como pontos principais da computação em um sistema e conectores
definem as interações entre estes componentes.
De acordo com PERRY e WOLF (1992), sistemas evoluem principalmente devido
a mudanças nos requisitos de negócio e são adaptados a novas funcionalidades. Uma
propriedade importante e freqüente durante o processo de evolução é o aumento da
resistência de um sistema em mudar, ou no mínimo sofrer pequenas alterações
(BROOKS, 1972). Este fato ocorre devido a dois problemas arquiteturais. Um destes
problemas é devido às violações de arquitetura. Neste contexto, as violações são
relações de dependência não permitidas na arquitetura da aplicação. O outro tipo de
problema arquitetural está relacionado ao desconhecimento dos conceitos de
arquitetura. Esta falta de conhecimento promove mais inadaptabilidade do que desastres
e resultam em uma deficiência de coerência e clareza, facilitando as violações de
arquitetura.
Um sistema em camadas é um padrão de design arquitetural no qual sua estrutura
pode ser decomposta em grupos de componentes nos quais cada grupo apresenta uma
função específica dentro do contexto da aplicação. Um exemplo é a arquitetura OSI
(OSI, 2011). Com a finalidade de padronizar o desenvolvimento de produtos para redes
de comunicação de dados, ela é divida em camadas hierárquicas. De acordo com a
figura 1 ilustrada abaixo, cada camada oferece um conjunto de serviços ao nível
superior, usando serviços da própria camada ou de níveis anteriores.
6
Figura 1. Arquitetura OSI
2.2 Geração de código
Hoje em dia existem inúmeros modos de classificar as abordagens MDA.
Encontramos em (CZARNECKI, HELSEN, 2003) a definição de duas principais
categorias de transformação: a transformação de modelo para código e a transformação
de modelo para modelo. A transformação de modelo para código consiste em possuir
um modelo como entrada para a geração de uma linguagem de programação específica.
A transformação de modelo para modelo consiste na tradução de modelos, sendo estes
instâncias de um mesmo metamodelo ou de metamodelos diferentes.
Embora este tema seja amplamente mencionado nesta área, não existe um estudo
profundo e uma discussão detalhada sobre a quantidade de código fonte necessária a ser
7
gerada. Nós entendemos que basicamente todas as abordagens MDA podem pertencer a
duas categorias: geração parcial do código fonte ou geração total do código fonte. Neste
capítulo serão apresentados alguns pontos importantes sobre estas duas categorias.
Primeiramente precisamos definir estas duas categorias. A geração total do código
permite que todo o código fonte seja gerado dos modelos não importando quando a
transformação ocorra. As modificações necessárias a aplicação são feitas diretamente
nos modelos, nunca no código fonte. Todo comportamento da aplicação pode ser
expressado através de modelos.
Diferentemente da abordagem anterior, a geração parcial do código suporta que
o mesmo seja alterado de algum modo. Estas customizações permitem expressar o
comportamento da aplicação que não pode ser descrito em modelos. Não é simples
discutir as vantagens e desvantagens para cada tipo de desenvolvimento. O ponto
positivo de um pode ser o ponto negativo de outro e vice-versa.
Por um lado, a geração total mantém o modelo e o código fonte realmente
síncronos. Em outras palavras, toda informação sobre a aplicação pode ser lida dos seus
modelos a qualquer hora. Ela também favorece a portabilidade caso um modelo
independente de plataforma seja aplicado, seguindo as premissas dos padrões da OMG.
Esta abordagem é utilizada na área de engenharia reversa porque todas as características
do código fonte podem ser mapeadas para algum elemento de modelo, sendo recriado
sem perda de informação.
Por outro lado, se algo precisa ser gerado de modo diferente, o processo de
transformação precisará ser alterado independente do tamanho, do impacto gerado pela
mudança ou do quanto poderia ser aproveitado aplicando o conhecimento de reuso de
softwares. Estas modificações não são raras, elas podem resultar de diferentes motivos
tais como aumentar o desempenho do sistema, ajustes em interfaces, problemas de
8
projeto, lógica de negócio entre outros. Além disso, eles podem variar de projeto para
projeto. Baseando-se em nossa experiência, outra vantagem da abordagem de geração
parcial é a falta de detalhe encontrado nas linguagens de modelagem atuais, mais
especificamente em uma das mais utilizadas, a UML. Ela não apresenta o mesmo nível
de detalhamento das linguagens de programação em alto nível como JAVA (JAVA,
2010), por exemplo. Estas questões, na prática, permitem que o uso da abordagem de
geração total do código bastante improvável na maioria das aplicações reais.
A geração de código parcial contém informações no código fonte que não estão
representadas nos modelos. Os pontos de implementação são fornecidos pelas
transformações para permitir aos desenvolvedores estenderem o comportamento
expressado pelos modelos através de códigos customizados. Apenas estas partes do
código podem ser alteradas manualmente. O sincronismo dos modelos e do código é
alcançado pelos artefatos gerados que não podem ser diretamente alterados.
A principal desvantagem é que se o comportamento expressado nos pontos de
implementação são relevantes por algum motivo, o único modo de resgatá-los seria
através do acesso direto ao código fonte. A geração parcial pode também sofrer do
mesmo problema da falta de detalhes nas linguagens de modelagem, embora em menos
intensidade do que a geração total, visto que os pontos de implementação existem para
poder suprir esta falta de detalhes.
Outro ponto positivo desta abordagem é o ganho de produtividade atingido pela
possibilidade de modificar e customizar o código fonte. Como mencionado
anteriormente, estas mudanças podem variar de projeto para projeto e para implementá-
las nos processos de transformação, não apenas seria difícil por razões proprietárias,
mas também devido a complexidade de serem empregadas na maior parte dos casos do
que simplesmente implementá-las no código fonte. Em relação à experiência que
9
adquirimos no desenvolvimento de aplicações reais de grande porte com MDArte, e de
acordo com (GEN, CHENG, 2002) que diz que o Princípio de Pareto pode ser usado
para objetivos de otimização, acreditamos que a abordagem ideal para maioria dos casos
seria a geração aproximada de 80% do código.
2.2.1 Abordagem MDA de geração total do código
O principal objetivo desta abordagem é definir um alto nível de abstração
representada por modelos para desenvolvedores e designers para construírem
aplicações. No entanto, a distância da implementação pode ser uma desvantagem
porque obriga os desenvolvedores a dependerem totalmente das transformações
fornecidas. Além disso, a aplicação gerada segue um padrão de arquitetura pré-
estabelecida definida por estas ferramentas de transformação. Alguma alteração
desejada na arquitetura ou até mesmo características específicas de plataforma que
sejam simples devem ser suportadas por transformações dos modelos. Apresentar um
alto desempenho e o código gerado executar todos os requisitos de negócio devem ser
atendidos por esta abordagem. Ela deve possuir uma linguagem independente de
plataforma que permita qualquer desenvolvedor focar no problema. Ela também deve
permitir que as ferramentas de transformação ou geração cuidem dos detalhes. Em
relação a problemas de layout, a parte visual da aplicação deve atender os usuários mais
exigentes.
Enquanto esta abordagem pode atender alguns casos específicos, de acordo com a
nossa experiência em ambientes de desenvolvimento de grandes aplicações, a
abordagem parcial é mais apropriada. A figura 2 abaixo descreve que o desenvolvedor
10
deve apenas modificar os modelos porque o código fonte é completamente gerado da
transformação.
Figura 2. Abordagem de geração total do código
Considerando uma aplicação cujo código é totalmente gerado a partir de
informações do modelo, o objetivo de detectar violações de arquitetura neste contexto
não está focado nas informações presentes no código fonte. O atendimento de todos os
requisitos da aplicação é descrito pelos elementos de modelo, portanto não há
intervenção dos desenvolvedores, ou seja, não existe uma customização nos pontos de
implementação.
A partir das referências previsíveis entre os artefatos, aplica-se a abordagem de
engenharia reversa para obtenção do grafo de dependência dos artefatos. Durante
processo de transformação dos modelos, ocorre o mapeamento da vinculação entre estes
artefatos e os elementos de modelo. Portanto utilizando ambas as informações, como
proposto nesta tese, será descrito a obtenção do grafo de dependência dos elementos de
11
modelo responsáveis pela geração destes artefatos. Cruzando estas informações com as
regras criadas pelo arquiteto, as violações em nível de modelo representam erros em
regras definidas durante o processo de transformação do modelo por um framework
MDA.
Portanto em um cenário onde se utiliza a abordagem de geração total do código, as
violações de arquitetura dos elementos de modelo representam erros descritos nas regras
de transformação descritas nos cartuchos (componentes) de uma ferramenta MDA. O
objetivo da representação de tais falhas é permitir que o arquiteto corrija estas regras de
transformação, evoluir os cartuchos do framework, visando um menor tempo de
processamento dos modelos, melhorando as relações entre os metafaçades, tornando a
arquitetura mais flexível para atender a novos requisitos. Metafaçades são artefatos
usados para prover acesso a elementos de um modelo.
2.2.2 Abordagem MDA de geração parcial do código
Como mencionado anteriormente, esta abordagem permite que a aplicação tenha
algumas partes customizáveis do código fonte através dos pontos de implementação.
Estas são áreas do código gerado que podem ser modificadas pelos desenvolvedores,
mas que não afetam as transformações seguintes e nem são afetadas por elas. Elas
devem ser utilizadas para expressar o comportamento que seria complexo para ser
descrito nos modelos, ou que simplesmente não é necessário de estar descrito. Embora
interfaces e toda visualização seja uma parte principal do sistema, ela não pode ser
descrita minuciosamente utilizando a principal linguagem de modelagem hoje, a UML.
Devido a este exemplo, pontos de implementação devem ser usados.
Exemplificando a dificuldade de descrever alguns comportamentos diretamente no
12
modelo, quando o número de passaporte é definido no modelo, não é necessário ter todo
o algoritmo de validação representado, o mesmo pode ser expressado diretamente no
código através dos pontos de implementação. Portanto, a figura 3 abaixo ilustra que o
desenvolvedor deve modificar os modelos e os pontos de implementação na abordagem
de geração parcial do código.
Figura 3. Abordagem de geração parcial do código
Em um cenário real, os pontos de implementação herdam o comportamento da
aplicação que não pode ser descrito no modelo. Neste contexto também representam a
possibilidade do desenvolvedor cometer alguma violação para atender um requisito de
um cliente em um prazo mais rápido, por exemplo.
Para o arquiteto, as informações da aplicação descritas tanto no modelo quanto no
código fonte são extremamente importantes. Utilizando as técnicas de engenharia
reversa, é extraído do código fonte o grafo de dependência dos artefatos. A partir das
13
transformações dos modelos em artefatos, é gerado o grafo de dependência dos
elementos correspondentes.
A aplicação das regras criadas pelo arquiteto em ambos os grafos permite uma
análise de violações no nível dos artefatos quanto no nível dos elementos descritos nos
modelos. Em relação às transições não previstas por ele e descritas no artefato, refletem
referências incorretas nos pontos de implementação, nos quais serão verificados os
motivos junto aos desenvolvedores da equipe. As violações, descritas no grafo no nível
de modelo, representam os erros detectados nas regras de transformação do modelo
pelos cartuchos do framework MDA responsável pela geração da aplicação.
14
3. Trabalhos Relacionados
Neste capítulo serão discutidos os trabalhos relacionados a todo o processo
envolvido na detecção de violações da arquitetura de uma aplicação. A proposta desta
tese é identificar tais falhas em uma ambiente de desenvolvimento que adota a
abordagem de geração parcial ou total do código. Nestes casos as informações descritas
no modelo e no código fonte são importantes para a análise das violações pelo arquiteto.
Em relação à recuperação das relações de dependência descritas no código fonte, a
seção 3.1 irá apresentar trabalhos na área de engenharia reversa, ou seja, as técnicas
utilizadas para recuperar informações descritas diretamente no código fonte da
aplicação. A seção 3.2 irá apresentar estudos relacionados à detecção de vários tipos de
violações em um sistema a partir de informações do modelo ou do código. Além disso,
serão discutidas as abordagens de classificação e identificações de padrões e a criação
de regras para descobrir tais violações.
3.1 Engenharia reversa
Veremos nesta seção abordagens de engenharia reversa aplicadas a sistemas
orientados a modelos. A maioria delas utiliza uma árvore de sintaxe abstrata construída
por um compilador usando uma gramática de linguagem comum. Esta árvore é
transformada em um modelo original a partir de transformações modelo para modelo.
A OMG está ativamente envolvida com a área de engenharia reversa para uma
arquitetura orientada a modelo, através de processos definidos em modernização
15
dirigidos a arquitetura (da sigla em inglês ADM, Architecture-Driven Modernization
(ADM , 2010)) como descrito em (ULRICH, 2004).
Mansurov e Campara (2005) propõem que a manutenção e evolução de uma
aplicação esteja dirigido a arquitetura ao invés do código. Para chegar a tal objetivo o
passo principal seria a introdução da modelagem no processo de desenvolvimento de
softwares. Assim eles focaram na extração destes modelos a partir do código fonte da
aplicação.
Uma árvore de sintaxe abstrata faz parte da construção de um framework para
representação natural do código fonte. Este trabalho é apresentado em (AL-EKRAM,
KONTOGIANNIS, 2005) no qual o XML (XML, 2010) é usado como linguagem
principal. Ainda baseado na evolução de uma árvore de sintaxe abstrata, uma anotação
genérica é proposta em (CEPA, MEZINI, 2006) com a finalidade de suportar modelos
independentes de plataforma.
BORONAT et al. (2005) apresentam um framework para migração automática de
sistemas legados para uma arquitetura orientada a modelo, usando uma lógica de
reescrita com sua engine de transformação. Seus resultados são modelos UML de um
sistema legado. Em (GANNOD, CAREY, 2004), uma abordagem incremental de
tecnologias orientadas a modelo baseadas em anotações Java é apresentada. A geração
de modelos MDA a partir de modelos proprietários é discutido em (DOYLE, 2005). Sua
abordagem envolve a reconstrução e normalização de modelos de representações de
banco de dados, que são subseqüentemente transformados em representações (MOF,
2010) usando Eclipse Modeling Framework (EMF, 2010). Uma abordagem incremental
de tecnologias orientadas a modelo é fornecida em (GANNOD, CAREY, 2004), neste
trabalho as anotações JAVA são utilizadas nas criações de modelo pela ferramenta
Eclipse Modeling Framework.
16
A combinação de parser com transformações de modelo está presente na
aplicação de engenharia reversa para uma abordagem MDA. KURTEV et al. (2002)
refere-se a este fato como ligações entre a gramática definida para um parser e as
transformações de modelo. Um framework genérico para tratar estas ligações é
discutido por Wimmer e Kramler (2006). Outras tecnologias de parser aplicadas na área
de engenharia reversa são apresentadas em (NICKEL et al., 2000, NICKEL , NIERE,
2001, NIERE et al., 2002, FAVRE, NGUYEN, 2005).
Figura 4. Etapas para aplicação da engenharia reversa em um sistema legado adotando a abordagem MDA (REUS et al., 2006).
Em (REUS et al., 2006) é aplicado um estudo de engenharia reversa em um sistema
legado utilizando a abordagem de arquitetura orientada a modelo. Os passos para tal
transformação estão ilustrados na figura 4 acima. Primeiramente é analisado o código
fonte da aplicação com o objetivo de determinar sua estrutura gramatical de acordo com
uma gramática específica. A árvore de sintaxe abstrata obtida é mapeada para um
modelo gramatical definido no MOF (2010). A partir deste modelo gramatical são
17
utilizadas transformações de modelo para modelo para a geração de árvores de sintaxe
abstrata genéricas, no qual as informações sobre o sistema legado podem ser
armazenadas em uma linguagem independente de plataforma. Mapeando esta árvore
genérica, novamente utilizando transformações de modelo para modelo, para modelos
UML com o objetivo de gerar novamente o código fonte ou para fins de documentação
seguindo a abordagem MDA.
Outra abordagem usada em engenharia reversa são os extratores de fatos (tuplas de
entidades e relacionamentos). Neste caso entende-se que entidades são classes, variáveis
e métodos e os relacionamentos são heranças, instanciações de classes e chamadas de
função. Um extrator de fatos para a linguagem Java é apresentado em (KAASTRA,
KAPSER, 2003). Com o objetivo de adotar a reengenharia em modelos dinâmicos,
BRIAND et al. (2003) geram diagramas de seqüência UML através do mapeamento das
interações em tempo de execução. ROUNTEV et al. (2004) analisa o fluxo de controle
diretamente no código fonte.
Com o framework Columbus (FERENC et al. , 2002) é possível aplicar engenharia
reversa em grandes projetos na plataforma C/C++. Ele gera diagramas de classe, uma
árvore de sintaxe abstrata e grafos de chamada. Além do mais, este framework suporta
reconhecimento de padrões de design. CPP2XMI (KORSHUNOVA et al., 2006) é
baseado em Columbus e extraem classes UML, diagramas de seqüência e diagramas de
atividade.
Estas abordagens não exploram os templates de geração de código para parser
diretamente. Templates são artefatos que possuem padrões para a geração de uma
linguagem de programação. A geração de texto baseado em templates é apresentada
como uma técnica padrão para o desenvolvimento de páginas web com plataforma PHP
18
(PHP, 2011) ou JAVA, por exemplo. Conseqüentemente esta mesma técnica é usada
para a geração de código a partir do modelo em uma abordagem MDA.
BORK et al (2008) usa o próprio template como uma gramática para realizar o
parser do código gerado, obtendo o modelo. Nesta abordagem, mudanças no template
não resultam em mudanças para executar o parser na árvore porque não é utilizado um
parser de linguagens comuns.
Figura 5. Processo de engenharia reversa utilizando templates como gramática (BORK et al, 2008)
O processo de geração de código utilizando templates é descrito na figura 5 acima,
no qual ocorre primeiramente a transformação do modelo original em uma estrutura de
token intermediária. Esta estrutura de token define a ordem de visita para os elementos
de modelo. Baseando-se no processamento do grafo de tokens, o código é gerado. Todo
token visitado é passado por uma cadeia de escritores de código, eles abrem o arquivo
de template especificado e passa o token e informações adicionais como contexto para
engine do template, a saída destes escritores é o próprio código fonte.
19
Figura 6. Exemplo da abordagem de engenharia reversa utilizando templates (BORK et al, 2008)
A abordagem de engenharia reversa utilizando templates é ilustrada na figura 6
acima. Dado um trecho de código fonte, o gerador de código verifica qual template foi
usado primeiro. Portanto, enquanto as informações entre o template e o código fonte são
cruzadas, as variáveis do template são atribuídas com fragmentos textuais do código
fonte para que o mesmo seja gerado novamente. Estas atribuições são utilizadas
posteriormente para reconstruir a camada de token intermediário e finalmente o modelo
propriamente dito.
As abordagens apresentadas nesta seção são limitadas pelos seus parsers. Em
relação ao parser de linguagens de alto nível, erros de sintaxe em um documento
causam problemas de manutenção visto que os templates usados em transformação de
modelo para texto são alterados. A abordagem de engenharia reversa utilizando
templates também pode falhar em reconhecer o código gerado com versões de templates
mais recentes. Além disso, o código customizado pelo programador pode evitar o
reconhecimento do template correspondente durante o processo de transformação.
20
Algumas características comuns dos trabalhos relacionados na área de engenharia
reversa são que coletam informações do modelo ou do código fonte. De acordo com o
nosso conhecimento, não existe um trabalho que utilize ambos para atingir seus
objetivos, visando à criação novos artefatos de projeto. Em um cenário real no qual
abordagem de geração parcial do código é amplamente utilizada, informações descritas
nos elementos de modelo e nas customizações dos pontos de implementação são
importantes para manutenção e evolução automática dos artefatos.
3.2 Detecção de violações
A automatização do processo de manutenção dos artefatos do projeto possibilita
um monitoramento contínuo da aplicação garantindo a aderência a arquitetura
estabelecida. O arquiteto define os padrões a serem utilizados pelos desenvolvedores
por diferentes meios como modelos e notações, visando identificar através dos
mapeamentos destas regras, transições não previstas que resultam em um impacto para
evolução do sistema durante o ciclo de vida do projeto.
Em (CIUPKE, 1999) para detectar problemas nos artefatos de uma aplicação, é
realizada uma pesquisa em um modelo para identificar padrões que representem estes
problemas. Eles estão descritos na forma de grafo, no qual serão aplicados consultas
sobre a existência destas violações. O resultado de tais consultas é uma parte do design
especificando a violação encontrada no sistema, sendo representada por um modelo
formal. Ainda neste trabalho são discutidas várias abordagens de criar as consultas no
modelo de design existente para a detecção de violações, incluindo suas vantagens. O
modelo pode ser lido como um grafo e as consultas se tornam algoritmos aplicados a
este grafo. Outra abordagem é que o modelo pode ser descrito como vários conjuntos e
21
suas relações, enquanto as consultas seriam expressões de álgebra relacional. Em outra
técnica de identificação de violações, o modelo pode ser descrito por proposições
lógicas e ser consultado por uma linguagem lógica.
Em softwares baseados em componentes, interfaces são separadas das
implementações e servem como contrato entre usuários e os desenvolvedores de
componentes. Uma melhor noção entre violações de interface e contratos por design é
apresentado em (Meyer B., 1988). Uma proposta para definir diferentes
responsabilidades para tratar as violações de arquitetura são discutidas por (LISKOV,
GUITAG, 1986, PERRY, 1989).
Em relação à prática, EDWARDS et al. (1998) aborda que muitas falhas em
aplicações baseadas em componentes ocorrem devido a violações de interface entre
estes componentes. Estas falhas ocorrem principalmente para atender requisitos de
reuso de um componente ou para atender aos requisitos do cliente. Em seu trabalho, eles
adotaram um exemplo de interface de componente, um template C++. O mapeamento
de tais violações é feito através de modelos matemáticos aplicados a esta interface.
Várias linguagens de especificação formais, como por exemplo, Z(VDM, 1990), e
Larch (GUTTAG et al. 1985) foram criadas para permitir que desenvolvedores
especifiquem o comportamento da aplicação independente das linguagens adotadas para
implementar estes sistemas.
Uma linguagem de expressões de restrição C++ (CHOWDHURY, MEYERS,
1993) permite aos desenvolvedores especificarem uma grande variedade de restrições
em um sistema, para detectar violações nestas restrições. Estas restrições podem ser
aplicadas em sintaxes complexas (restrições estilísticas) e semânticas (restrições de
implementação e de design). Estes diferentes tipos de restrições são definidos abaixo:
22
• Restrições estilísticas: Todos os nomes das classes devem começar com letra
maiúscula. As maiorias das equipes de desenvolvimento adotam algum tipo de
convenção de nomes para identificadores, fugir desta convenção também pode ser
considerada uma violação.
• Restrições de implementação: Se uma classe declara um membro, ela também
deve declarar um operador e um construtor. Falhas em aderir a esta restrição levam a
um comportamento incorreto do programa.
• Restrições de design: A função M em uma classe C deve ser sobrescrita em
todas as classes derivadas de C. As restrições de design são específicas para uma
aplicação ou biblioteca.
A detecção de violações de arquitetura é uma parte muito importante para a
manutenção e evolução de uma aplicação. “Bad smells” e métricas são maneiras para
identificar necessidades de refatoração do código.
“Bad smells”, problemas encontrados nos artefatos de um projeto, são abordados
em (FOWLER, 1999, KERIEVSKY, 2004, ROOCK, LIPPERT, 2006), no entanto a
desvantagem deste método está em ser aplicado em grupos pequenos de classes
relacionadas, além de serem difíceis de serem quantificados, portanto difíceis de serem
priorizados. Uma das principais alternativas a procura de “Bad Smells” são as métricas
de código (FENTON, PFLEEGER, 1997). Uma desvantagem desta abordagem é que
geralmente não levam em consideração os aspectos relativos à arquitetura e ao design
da aplicação. Isto leva a concluir que a relevância a refatorações arquiteturais são
questionáveis.
23
Portanto para realizar uma refatoração de alto impacto na aplicação, a análise das
violações de arquitetura complementa as abordagens de detecção de falhas citadas
anteriormente (BOURQUN, KELLER, 2007). Tal análise é realizada em quatro etapas.
Primeiramente um modelo descrevendo a arquitetura da aplicação é criado. A partir
deste modelo são mapeados todos os pacotes da aplicação para seus respectivos
componentes do modelo de arquitetura. Após este mapeamento, são checadas as
referências dos pacotes da aplicação contra as referências dos modelos de arquitetura.
Alguma transição não permitida pelo modelo arquitetural constitui uma violação de
arquitetura. Baseando-se nessas falhas, é aplicado de uma melhor maneira para refatorar
o código fonte.
Umas características comuns dos trabalhos relacionados na área de detecção de
violações de arquitetura é que aplicam regras diretamente no código fonte ou nos
elementos de modelo. De acordo com o nosso conhecimento, não existe um trabalho
que utilize ambos para atingir seus objetivos, possibilitando analisar as violações
cometidas pelos artefatos e também pelos elementos de modelo responsáveis pela sua
geração destes artefatos.
Baseando-se em uma abordagem de geração parcial do código, a visualização do
grafo de dependência dos artefatos e a identificação de transições que constituem
violações de arquitetura, resultam em evoluções diretamente no código ou no
framework responsável pela geração da aplicação.
Em relação à abordagem de geração total do código a partir do modelo, a
visualização do grafo de dependência dos elementos de modelo, gerado a partir das
relações de dependência entre os seus respectivos artefatos, permite ao arquiteto mapear
as falhas nas regras de transformação e conseqüentemente evoluir a arquitetura do
próprio framework MDA responsável pela geração da aplicação.
24
4. A proposta
O estabelecimento de uma documentação associada ao desenvolvimento e
manutenção automática de seus artefatos durante todo o ciclo de vida do projeto,
apresenta várias vantagens. A análise do modelo pela equipe de desenvolvimento
permite uma melhor compreensão do código fonte (MAYRHAUSER,VANS, 1993,
1994, 1996, SOLOWAY, EHRLICH, 1994 ). Na indústria do software encontramos
inúmeros sistemas legados complexos, no qual a restauração da documentação nestes
casos é essencial para a evolução destes sistemas (BIGGERSTAFF, 1989,
CHIKOFSKY, II, 1990). Em relação a especificações de requisitos, o mapeamento entre
este tipo de documentação e o código fonte é essencial para identificar as áreas do
código que implementam funcionalidades específicas do usuário (KONCLIN,
BERGEN, 1988, PINHERO, GOGUEN, 1996, RAMESH, DHAR, 1992).
A importância da documentação do código também está presente na área de análise
de impacto, no qual o principal objetivo é a identificação de componentes afetados por
uma mudança proposta (ARNOLD, BOHNER, 1993, FYSON, BOLDYREFF, 1998,
TURVER, MUNRO, 1994). Estas alterações podem ter sido resultantes da própria
documentação, ou do próprio código fonte, sendo propagadas para outros componentes.
Em um cenário de desenvolvimento orientado a modelo, a manutenção automática
de artefatos de projeto é feita a partir de informações extraídas diretamente do código
fonte e dos elementos de modelo. Esta proposta de automatização é ilustrada na figura 7
a seguir:
25
Figura 7. A proposta
As equipes de desenvolvimento adotam os modelos para representar o
comportamento da aplicação. Os pontos de implementação são utilizados quando o
modelo falha em representar tal comportamento. Esta é a principal razão pela qual
extrair informações de ambos é necessário para automatizar a manutenção de artefatos
do projeto.
As informações extraídas podem ser representadas por um modelo,
preferencialmente usando UML. O processamento destas informações permite a
possibilidade da criação e da manutenção dos artefatos do projeto. Exemplos destes
artefatos são métricas de software como pontos de função, grafos para análise de
impacto e/ou violações de arquitetura.
O foco desta tese é automatizar a identificação de violações de arquitetura nos
cenários de desenvolvimento orientado a modelo com geração completa ou parcial de
código, sendo assim um caso específico da proposta apresentada anteriormente.
26
As informações extraídas dos pontos de implementação são as relações de
dependências existentes entre os artefatos, enquanto as extraídas diretamente dos
modelos são as vinculações de seus elementos aos artefatos gerados.
A partir da análise destas informações, é realizada a manutenção automática dos
grafos de dependência e dos grafos de violações de arquitetura. A figura 8 abaixo ilustra
a aplicação da proposta apresentada anteriormente em um cenário específico, no caso a
manutenção automática destes grafos a partir de informações extraídas do código e do
modelo.
Os grafos de dependência dos artefatos e dos elementos de modelo oferecem ao
arquiteto uma real análise do conjunto atual das transições existentes nestes dois níveis
da aplicação. Os grafos de violações de arquitetura dos artefatos e dos elementos de
modelo permitem ao arquiteto validar, se o código inserido nos pontos de
Figura 8. Aplicando a proposta para a manutenção
automática de artefatos do projeto
27
implementação e o conjunto atual de transformações de modelo obedecem às regras
arquiteturais pré-estabelecidas.
De acordo com a proposta descrita anteriormente, a seção 4.1 abordará a vinculação
dos artefatos aos elementos de modelo. A seção 4.2 discutirá a extração e a geração do
grafo de dependência dos componentes, a partir da análise do código fonte. A partir
deste grafo, a seção 4.3 apresentará a especificação da arquitetura. Além disso, a
definição dos grupos e das regras arquiteturais envolvendo estes grupos, com objetivo
de detectar violações de arquitetura também serão discutidos.
4.1 Vinculação dos artefatos aos elementos de modelo
Adotando-se a geração total ou parcial do código, o arquiteto analisa a
documentação que descreve o mapeamento dos elementos de modelo e os artefatos
gerados por eles. Estas informações podem estar representadas em um modelo, com o
objetivo de evoluir a arquitetura da aplicação, a partir das regras de transformação
descritas nos componentes do próprio framework responsável pela geração do seu
código.
Durante o processo de transformação, um framework MDA possui componentes
responsáveis por armazenar as informações dos elementos descritos no modelo em
classes e as relações de dependência entre elas tornam as informações destes elementos
navegáveis.
Para construir um mapeamento entre eles, é preciso identificar durante o
processo de transformação quando ocorre a geração de tal artefato de acordo com as
classes associadas para sua criação. Existem vários tipos de associação relacionados à
geração de um determinado artefato. A figura 6 abaixo ilustra a relação no qual um
28
determinado elemento do modelo é responsável por gerar um artefato especifico. Este é
o caso mais comum presente no processo de transformação do modelo por um
framework MDA.
O arquiteto define que um determinado artefato será gerado sempre que existir o
elemento de modelo correspondente. Um exemplo deste tipo de relação é a modelagem
de uma classe da camada de domínio. As classes que representam a camada de domínio
de uma aplicação são identificadas por um estereótipo, conforme ilustrado na figura 9
por <<Entity>>, portanto a regra definida pelo arquiteto para a geração de um classe
Java associada a este elemento é que a mesma siga o padrão de design JavaBeans por
exemplo.
Figura 9. Um elemento de modelo é responsável pela geração de um artefato
29
O tipo de associação ilustrado pela figura 10 abaixo engloba informações presentes
em vários elementos de modelo para a geração de um determinado artefato. Neste caso
durante o processo de transformação, todas as informações pertencentes a diferentes
elementos de modelo são agrupadas para a geração de um único artefato.
Um exemplo neste caso pode ser o mapeamento das URLs que definem de modo
único, o inicio de cada caso de uso. Estas informações estão descritas em cada elemento
de modelo que apresenta o estereótipo <<Use Case>>, portanto o arquiteto pode definir
que para a arquitetura da aplicação, seria importante encapsular estas informações em
uma classe utilitária da camada de vista, por exemplo. Aplicações que utilizam o
framework Struts (STRUTS, 2010), possuem um único arquivo de configuração no qual
todas as Actions do sistema estão mapeadas. Estas informações foram geradas a partir
de todos os casos de uso descritos no modelo.
30
Figura 10. Um artefato é gerado a partir de informações descritas em vários elementos de modelo
Outra associação é o mapeamento de um determinado elemento de modelo para
vários artefatos. Este elemento é responsável pela geração de um conjunto de artefatos,
conforme ilustrado pela figura 11 abaixo. Neste caso temos um exemplo no qual o
elemento << Use Case >>, representando um caso de uso da aplicação, é responsável
por gerar um artefato contendo todos os atores envolvidos, outro contendo os cenários
principais e secundários, além de um artefato responsável por documentar todos os
atributos deste caso de uso.
31
Figura 11. Vários artefatos são gerados a partir de um único elemento de modelo
O último tipo de associação é quando o artefato a ser gerado por um determinado
componente do framework independe do elemento de modelo. Neste caso o arquiteto o
define como essencial a aplicação, apresentando uma funcionalidade específica na
camada que ele atua, independente das informações descritas no modelo.
Um exemplo neste caso pode ser dado como classes utilitárias que apresentam
funções comuns amplamente utilizadas pelos desenvolvedores durante o ciclo de vida
do projeto. Neste caso podemos criar uma classe utilitária com funções específicas de
tratamento de textos, por exemplo. O objetivo dela é auxiliar os desenvolvedores com o
tratamento de Strings de acordo com os requisitos definidos do projeto. Este tipo de
associação está ilustrado na figura 12 abaixo.
32
Figura 12. A geração de um determinado artefato independe dos modelos
4.2 Extração de dependências
Para o arquiteto da aplicação, as informações presentes nos pontos de
implementação são importantes para análise de um sistema gerado a partir do modelo.
Estes pontos representam a liberdade dos desenvolvedores de atenderem aos requisitos
especificados pelo cliente, expressam um comportamento do sistema que não pode ser
descrito em modelos.
Com a finalidade de avaliar a aderência das relações de dependência na arquitetura
pré-estabelecida, as informações extraídas diretamente do código fonte e dos modelos
são importantes. A figura 13 ilustra o processo de extração das relações de dependência.
33
Figura 13. Extração e filtragem das dependências
Primeiramente é feito um processo de varredura no código-fonte (artefato) da
aplicação. Para tal finalidade, um extrator de dependências é utilizado. Esta ferramenta
utiliza a técnica de engenharia reversa, no qual em uma árvore de sintaxe baseada em
uma determinada gramática, é realizada o parser do código identificando as transições
entre os artefatos. O resultado desta extração é um grafo de dependência inicial de
todos os artefatos da aplicação. Este grafo contém transições relevantes para a análise
do arquiteto e outras irrelevantes dependendo do contexto no qual são analisadas.
Portanto a documentação específica de plataforma gerada pelo extrator de dependências
deve ser modificada para atender aos interesses do arquiteto. Um processo de filtragem
das informações descritas nesta documentação deve ser realizado para as relações
irrelevantes descritas neste grafo.
34
Antes de realizar este processo de filtragem, deve ser identificado quais artefatos
são gerados a partir de quais elementos de modelo. Uma ferramenta MDA gera os
artefatos utilizando um modelo como entrada. Conforme explicado anteriormente na
seção 4.1, durante o processo de transformação do modelo, ocorre o mapeamento entre
os artefatos e os elementos de modelo responsáveis pelas suas gerações. Esta vinculação
é descrita em uma documentação independente de plataforma.
A partir deste mapeamento, é realizado o processo de filtragem no qual sabemos os
artefatos realmente gerados pela ferramenta MDA e aqueles que constam nos filtros de
eliminação definidos pelo arquiteto. Estas informações são cruzadas com as
identificadas pelo extrator. Portanto os artefatos criados manualmente, ou seja, aqueles
que não foram mapeados durante a transformação do modelo e não constam nos filtros
de eliminação, não são eliminados neste processo.
O resultado desta etapa é uma documentação independente de plataforma,
contendo apenas as relações de dependência extraídas dos artefatos relevantes para a
detecção de violações de arquitetura. Considerando o mapeamento dos artefatos aos
elementos de modelo, esta documentação também contém as relações de dependência
extraídas dos elementos de modelo, a partir do grafo dos artefatos.
4.3 Especificação da arquitetura
Em uma abordagem de geração parcial do código, mais utilizada em cenários reais
por razões citadas no capítulo 2, as customizações através dos pontos de implementação
são a chave para o desenvolvimento de sistemas gerados por ferramentas MDA. No
entanto, elas podem cometer violações desde que não atendam as regras de arquitetura
definidas pelo arquiteto. Em relação a abordagem de geração total do código a partir do
35
modelo, as violações da arquitetura descritas no código e mapeadas pelo arquiteto,
existem devido as evoluções nas regras de transformação do modelo.
Em relação ao framework MDA, o arquiteto é responsável por definir as regras de
transformação do modelo em código fonte. A partir destas regras de transformação, toda
a arquitetura do sistema é estabelecida através de componentes do framework.
Em cada componente o arquiteto possui a responsabilidade de definir os artefatos
que serão gerados a partir de elementos do modelo. As relações de dependência ente
estes artefatos variam de acordo com o tipo de arquitetura definida para a geração de
sistemas. De qualquer forma um conjunto de artefatos, nomeados nesta tese como
grupos, são criados devido uma função lógica dentro da aplicação, como por exemplo, a
composição de uma camada da arquitetura. Como uma camada se comunica com outras
camadas, grupos apresentam relações de dependências com outros grupos.
Ao projetar a arquitetura através de um framework MDA, o arquiteto define o
grupo ao qual aquele artefato pertence. Esta classificação pode estar descrito
diretamente no modelo como uma informação adicional para um elemento, no caso dele
gerar um artefato. Outro caso seria estarem documentados diretamente nos componentes
de transformação do framework. As definições de grupo podem ser recuperadas a cada
transformação do modelo a partir da vinculação dos artefatos aos componentes,
conforme explicado na seção anterior.
O arquiteto deve estabelecer as transições permitidas entre estes grupos. Muitas
destas referências são estabelecidas nos componentes de transformação de uma
ferramenta MDA. Como mencionado anteriormente, o uso de customizações via pontos
de implementação representam um risco para violações de arquitetura aparecerem. O
arquiteto deve estar ciente das mudanças que estas customizações podem criar e como
36
corrigi-las se necessário. Portanto é importante a criação de um novo conjunto de
regras, na forma de novos grupos ou subgrupos e suas transições.
Estas novas regras arquiteturais podem ser documentadas através de um modelo
UML. Um diagrama de componentes pode representá-las, descrever através deste
modelo o conceito de novos grupos, subgrupos e suas relações.
Um diagrama de componentes é um grafo de componentes conectados através de
relações de dependências. Ele mostra a estrutura do modelo de implementação,
apresentado como um conjunto de elementos como componentes, subsistemas e seus
relacionamentos, conectados entre si. As estruturas a seguir são adequadas para
ilustração em diagramas de componentes:
• Sistemas e suas dependências de importação.
• Sistemas organizados em camadas.
• Componentes e suas dependências de compilação e em tempo de execução.
• Estrutura de componentes (por exemplo, para ilustrar o uso típico de um
componente).
Em relação às regras arquiteturais, o foco é identificar arquivos de código-fonte e
suas dependências de compilação. Os componentes são os grupos compostos por um ou
mais artefatos. As relações de dependência entre os componentes representam as
transições entre os respectivos grupos.
37
Figura 14. Diagrama de componentes
A figura 14 acima ilustra subgrupos ou novos grupos definidos pelo arquiteto e suas
relações de dependência. Além disso, também ilustra os grupos estabelecidos durante a
elaboração da arquitetura a ser gerada pelo framework MDA a partir de um modelo.
Ao implementar os componentes de uma ferramenta MDA para geração dos
artefatos de uma aplicação, alguns grupos já estão presentes na arquitetura estabelecida
pelo arquiteto. Este caso é ilustrado pelo grupo A depender diretamente dos elementos
que compõe o grupo B, ou seja, ao gerar a aplicação a partir de informações descritas no
modelo, encontramos artefatos, gerados a partir do modelo, do grupo A referenciando
artefatos do grupo B.
De acordo com os requisitos de negócio que o sistema deva atender, ele pode julgar
necessário que um subconjunto do grupo A dependa de qualquer artefato pertencente ao
grupo C. Este subconjunto também pode ser composto de um único artefato. Este caso é
38
exemplificado pela relação de dependência que o artefato b pertencente ao grupo B
referencia a um elemento do grupo C.
Existe a possibilidade de definir relações para um conjunto cujo seus integrantes
pertencem a grupos diferentes. Portanto definimos o conceito de novo grupo, ou seja,
um novo conjunto de elementos formado por grupos existentes. Todos os elementos de
um grupo podem pertencer a este novo grupo ou somente um subconjunto dele. Esta
descrição é ilustrada por um novo grupo formado por artefatos dos grupos A, B e C que
dependem diretamente de qualquer elemento pertencente ao grupo D.
Adotando como exemplo a relação de dependência entre o novo grupo e o grupo D,
encontramos um tipo de transição geral por englobar qualquer elemento de ambos os
grupos estabelecidos. Entretanto, podemos explicitar as transições mais restritas, ou
seja, definir relações de dependência entre artefatos específicos. Um determinado
subconjunto ou até mesmo um artefato do novo grupo deve depender somente de um
artefato de outro grupo.
A relação de dependência restrita entre um artefato do novo grupo com um artefato
do grupo D é exemplificado também na figura 9. Nesta dissertação a regra que descreve
uma transição específica substitui a regra geral, ou seja, para qualquer elemento do novo
grupo, exceto um, pode referenciar qualquer elemento do grupo D, exceto um também.
Os artefatos e a relações de dependência entre eles compõem a exceção a ser seguida,
isto é, a regra geral vale somente para os demais integrantes deste grupo.
39
5. Implementação
Atualmente vários projetos utilizam o framework MDArte. Nestes projetos sempre
houve a necessidade de alguma ferramenta que pudesse extrair as dependências do
código fonte com objetivo de construir um grafo de dependência de componentes. Esta
informação ajudaria em melhorar a análise da aplicação em ambos os temas de análise
de impacto e validação de padrões arquiteturais. Como o framework MDArte também
aplica a abordagem de geração parcial, as informações exigidas para realizar tais tarefas
estão distribuídas entre o modelo e os pontos de implementação.
Existem extratores de grafo de dependência para aplicações Java (neste trabalho foi
adotado a linguagem Java como plataforma principal para as transformações modelo-
código), no entanto o extrator sozinho não é tão útil quanto poderia ser com métodos de
pós-processamento, para filtrar elementos apropriados aos objetivos de manutenção
automática dos artefatos do projeto.
Neste caso teste foi utilizado a aplicação Dependency Finder (DEPENDENCY
FINDER, 2011) para extrair as dependências do código fonte. Ela é um conjunto de
ferramentas para analisar código Java compilado. Em resumo é uma aplicação de
análise de dependência que extrai grafos de dependência e também minera estes dados
de acordo com os critérios adotados pelo analista.
Este capítulo aborda a implementação da proposta em um cenário de geração
parcial do código a partir do modelo. Além disso, apresenta que os integrantes do
projeto responsáveis por definir a arquitetura da aplicação possuem uma relação estreita
com a proposta discutida no capítulo 4. Esta relação será construída ao longo das
discussões apresentadas nas seções deste capítulo.
40
A seção 5.1 apresenta a matriz de rastreabilidade como sugestão para mapear a
vinculação dos artefatos aos elementos de modelo (proposto na seção 4.1). Em um
cenário de desenvolvimento que adota a abordagem de geração parcial, no qual será
adotado para o nosso caso teste, informações vindas do código e do modelo são
importantes para o arquiteto, portanto precisam ser mapeadas.
A seção 5.2 aborda como o arquiteto define o conceito de grupos, os critérios de
classificação dos artefatos gerados nestes grupos, baseando-se na arquitetura gerada
pelo framewrok MDA, sugerindo uma especificação de arquitetura conforme a proposta
na seção 4.3.
A última seção (5.3) discute a construção do grafo de dependência final dos
artefatos, após alguns filtros determinados pelo arquiteto. A partir da matriz de
rastreabilidade, o grafo de dependência dos elementos correspondentes também é
apresentado. Esta seção propõe uma implementação proposta na seção 4.2 sobre
extração de dependências.
41
5.1 A construção da Matriz de Rastreabilidade
Figura 15. Visão arquitetural do MDArte para a geração de artefatos a partir do modelo
Primeiramente será apresentado de forma geral o processo de geração de artefatos a
partir do modelo pelo MDArte. Este framework compreende um conjunto de cartuchos
AndroMDA com diversas soluções de projeto e arquiteturas incorporadas nos
procedimentos de transformação de modelos seguindo a abordagem MDA.
De acordo com a figura 15 ilustrada acima, o MDArte Core refere-se ao engine do
framework sendo responsável por gerenciar todos os componentes (cartuchos) da
ferramenta. A engine possui como tarefas descobrir os cartuchos e repositórios
utilizados e mapear os elementos de modelo para os metafaçades.
42
Metafaçades são façades (ALUR et al., 2001) usados para prover acesso a modelos.
Como representam um ou vários elementos de modelo, devemos aplicar um
mapeamento de representação da linguagem UML estendida para os metafaçades
através de um arquivo de configuração. Metafaçades podem ser modelados e gerados
pelo próprio MDArte, usando um cartucho específico. Portanto, metafaçades são usados
para a transformação e geração deles próprios.
Os Metafaçades têm como finalidade principal fornecer uma API orientada a
objetos que permita aos templates acesso fácil as informações dos modelos. Usando
metafaçades, os templates se tornam muito mais simples porque a inteligência e a
responsabilidade de transformação é centralizada em objetos Java, e não em linguagens
script de template.
Antes de passar para o processamento dos cartuchos, as seguintes tarefas são
realizadas pela engine:
• O mapeamento dos elementos do modelo em metafaçades. A engine procura
por todos os componentes de mapeamento de metafaçades e para cada elemento do
modelo aplica o mapeamento. Geralmente este mapeamento é feito via um estereótipo
presente em um profile. O resultado desta etapa é uma lista de metafaçades disponíveis.
• Também são executadas as validações das restrições OCL, Object Constraint
Language (OCL, 2010) presentes nos metafaçades disponíveis. Estas restrições visam
garantir que as informações modeladas sejam consistentes em relação às regras que
expressam padrões de modelagem.
Os cartuchos são responsáveis por mapear o modelo para uma plataforma
específica. Estes selecionam os metafaçades que irão utilizar e os associa a templates,
gerando vários artefatos baseados em texto. Por exemplo, estes componentes processam
elementos de modelo que apresentam um determinado estereótipo (<< Entity>> ou
43
<<ValueObject>>) ou elementos que atendam a certas regras inferidas do modelo como
todos os componentes que apresentam dependências a um determinado componente de
serviço <<Service>>.
Durante o processamento de transformação do modelo em um cartucho, o conjunto
de metafaçades disponíveis e todas as características de geração de um determinado
artefato estão descritas no próprio cartucho. Segue abaixo tais características:
• O conjunto de metafaçades que serão utilizados pelo template para gerar um
determinado artefato.
• O caminho completo do artefato a ser gerado.
• O caminho lógico onde o cartucho irá gerar tal artefato dentro da aplicação.
• Se o artefato deve ser sobrescrito a cada transformação do modelo.
• Se o artefato está associado a um ou mais elementos de modelo.
• Se o artefato deve realmente ser gerado vazio caso nenhum elemento de modelo
seja encontrado.
• Processamento de arquivos que não passam pela engine de templates chamados
recursos.
Algumas das informações descritas a seguir são extraídas a cada processamento do
template de cada cartucho. Segue abaixo tais informações:
• O nome completo do artefato gerado de modo que sua identificação seja
única na aplicação.
• O conjunto de elementos do modelo responsável por gerar tal artefato, caso
existam.
• Se o artefato é um ponto de implementação. Esta característica é definida
pelo fato dele ser sobrescrito a cada processo de transformação ou não.
• A qual grupo definido pelo arquiteto do MDArte tal artefato pertence.
44
Baseando-se nas informações extraídas, é construída a matriz de rastreabilidade.
Ela tem como objetivo mapear a relação entre os artefatos gerados e os elementos de
modelo, caso existirem, responsáveis por sua geração. Estas relações são identificadas
durante o processo de transformação do modelo, especificamente a cada processamento
de um template de um determinado cartucho, ou seja, a cada execução de uma regra de
transformação por ele. A implementação das construções das vinculações entre
elementos de modelo e os artefatos gerados na engine do MDArte está descrito no
anexo 1.
A figura 16 exemplifica uma parte da matriz de rastreabilidade extraída do modelo
de nossa aplicação teste. Nesta matriz observamos que um elemento de modelo é
responsável por gerar mais de um artefato, como por exemplo, o elemento <<Service>>
PaisHandler. No entanto outros artefatos como a classe UtilServiceHandlerBeanImpl
são gerados independente de qualquer elemento de modelo.
45
Figura 16. Matriz de Rastreabilidade
5.2 A definição dos grupos dos artefatos
O framework MDArte projetado atualmente é responsável por gerar aplicações cuja
arquitetura é baseada em camadas. Sua estrutura é baseada em cartuchos específicos
para gerar artefatos que irão compor cada camada da aplicação.
O arquiteto do MDArte é responsável por definir a função de cada artefato a ser
gerado pelo framework. Ele projeta toda a arquitetura dos componentes da aplicação ao
definir quais requisitos devem ser implementados nos templates e em qual camada do
sistema.
46
Baseando nestas informações, a definição de um artefato pertencer a um
determinado grupo pode apresentar três níveis de classificação a serem adotados pelo
arquiteto:
• Classificação geral: Cada cartucho é responsável por gerar seus
conjuntos de artefatos. O grupo ao qual tal artefato irá pertencer é escolhido pela
finalidade do cartucho.
• Classificação pelo caminho lógico: O grupo de um artefato é definido
pela camada no qual ele será gerado dentro da aplicação.
• Classificação específica: Aquele artefato pertence a um determinado
grupo.
Figura 17. A definição dos grupos baseado na estrutura dos cartuchos do framework MDArte
47
A figura 17 ilustra o mapeamento dos grupos e de seus artefatos. A cada
processamento das informações do modelo por um cartucho específico, é verificado
para aquele artefato a ser gerado qual critério de classificação foi adotado para ele. O
anexo 2 descreve a classe responsável por conter as regras de classificação dos artefatos
em grupos. Baseando-se no conceito de grupos, a arquitetura da aplicação pode ser
orientada a domínio, a nossa aplicação teste que será discutido neste capítulo e no
capítulo 6 apresenta a divisão em camadas, como um exemplo de domínio.
Primeiramente é verificado se este artefato pertence a um conjunto de nomes cujos
grupos já estão definidos. Caso ele não pertença a nenhum grupo, é verificado se o
caminho lógico que define onde este artefato será gerado está mapeado para um
determinado cartucho. Caso este mapeamento não ocorra, é verificado qual cartucho
está sendo processado naquele instante e este artefato passa a pertencer ao grupo padrão
estabelecido para tal cartucho.
Tomemos como exemplo o artefato “A” sendo gerado pelo cartucho Struts. Este
cartucho é responsável por gerar uma aplicação web utilizando a plataforma J2EE. A
engine primeiramente irá verificar se este artefato pertence a um conjunto pré-
estabelecido pelo arquiteto de artefatos que pertencem ao grupo visão. Caso o artefato
“A” não pertença a este grupo, é recuperada uma propriedade do template que indica em
qual camada da aplicação este artefato será gerado (caminho lógico). O arquiteto
classificou todas estas propriedades pertencentes a cada cartucho. Caso esta propriedade
classifique que os artefatos futuramente gerados pertencerão ao grupo visão, “A” será
inserido neste grupo.
Caso esta propriedade não esteja mapeada, o cartucho apresenta uma propriedade
(namespace) que o identifica de modo único para a engine. Assim de modo geral os
artefatos são classificados de acordo com o objetivo de cada cartucho. Portanto se o
48
artefato “A” não for mapeado pelas características mencionadas anteriormente, ele é
classificado de modo geral de acordo com o cartucho responsável pro sua geração.
5.3 A extração do grafo de dependência
Para a geração do grafo inicial relativo ao caso teste, foi utilizada a ferramenta
Dependency Finder para extrair as dependências da aplicação. O grafo de dependência
inicial é composto pelas relações em nível de pacote, classe e seus métodos. Estas
relações são extraídas passando como entrada de dados o código fonte da aplicação. O
resultado desta análise é um arquivo XML contendo as dependências entre os pacotes,
as classes pertencentes a estes pacotes e os métodos destas classes.
No entanto nem todas as relações extraídas pertencentes aos artefatos da
aplicação são importantes para uma futura análise do arquiteto, como relações a
bibliotecas externas, por exemplo. Neste caso teste foram implementadas referências
externas, como por exemplo, bibliotecas específicas para geração de relatórios em PDF
ou para a execução do protocolo de transferência de arquivos. As dependências aos
pacotes, classes e seus métodos pertencentes a estas APIs também foram extraídos pelo
extrator.
Algumas referências internas ao caso teste não são relevantes para a detecção de
violações de arquitetura. Um exemplo disto vem do conceito da própria plataforma
Java. A classe Object é a raiz na hierarquia das classes JAVA. Como toda classe possui
Object como super classe, todos os objetos implementam os métodos dela. Devido a
este conceito, o Dependency Finder também extrai esta relação para toda classe da
aplicação que a referencia. Além disso, referências relacionadas a demais classes que
49
compõem a arquitetura J2EE (J2EE, 2011) também são extraídas como chamadas de
métodos ou instâncias da classe String por exemplo.
Visando descobrir as violações de arquitetura cometidas pelas classes da
aplicação, foi elaborado um arquivo XML com objetivo de filtrar a saída inicial do
extrator de dependências. Após a execução destes filtros, apenas seriam descritas as
relações entre as classes da própria aplicação, eliminando as dependências entre os
métodos da classe por não ser o escopo deste trabalho.
Para filtrar as informações necessárias para construir o grafo final de
dependências dos componentes, os seguintes filtros também foram adotados:
• Eliminar qualquer referência externa não relevante segundo critérios do
arquiteto.
• Eliminar qualquer referencia interna prevista que não seja importante para o
arquiteto.
• Identificar os artefatos que foram criados manualmente pelos desenvolvedores
com objetivo de atender um determinado requisito da aplicação.
• Identificar os artefatos gerados pelo framework MDArte.
Definidos os critérios de filtragem dos artefatos, a figura 18 abaixo ilustra a
classificação destes artefatos durante o processo de construção do grafo de dependência.
50
Figura 18. Classificação dos artefatos
O conjunto chamado Dependency Finder indica todos os artefatos cujas
dependências foram extraídas por esta ferramenta. A entrada do volume de dados é
definida pelo arquiteto, ou seja, se será feita a análise de toda a aplicação ou de apenas
um módulo dela. Compondo a figura, o outro conjunto chamado MDArte ilustra todos
os artefatos da aplicação que foram gerados por esta ferramenta MDA a partir de
informações descritas no modelo ou como recursos de um cartucho.
De acordo com o filtro adotado, foram identificados três tipos de artefatos
ilustrados na figura 18.
Primeiramente as referências das bibliotecas externas utilitárias ou pertencentes à
plataforma J2EE são eliminadas. Estes artefatos estão presentes no grupo Dependency
Finder por serem extraídos por esta ferramenta. Eles não foram gerados pelo framework
51
MDArte, no entanto estão sendo referenciados indiretamente e analisados pelo extrator
de dependências. Eles não estão inclusos em nenhum critério de filtragem relativo as
transições externas ou internas relevantes, ou seja, não apresentam importância para a
análise de violações de arquitetura.
Ainda no grupo Dependency Finder, existem artefatos que não foram gerados pelo
framework MDArte, entretanto são importantes para a aplicação de acordo com os
filtros estabelecidos. Esta importância significa que estes artefatos foram criados
manualmente pelos desenvolvedores para atender um determinado requisito da
aplicação.
Os artefatos pertencentes ao grupo MDArte foram gerados diretamente por este
framework. O caso teste descrito nesta tese envolve todos os artefatos do sistema, no
entanto a figura ilustra, através da interseção de uma parte dos conjuntos, a
possibilidade de que o conjunto Dependency Finder não varrer todos os artefatos da
aplicação. Isto é devido ao fato da aplicação poder ser modularizada, ou seja, apenas
uma parte da aplicação poderá atender aos interesses do arquiteto durante uma fase
específica do ciclo do projeto.
Com todos os artefatos mapeados é realizado o processo de filtragem para a
recuperação dos artefatos que serão realmente importantes para o arquiteto. A
representação do grafo de dependência final é exemplificada na figura 19 abaixo:
52
Figura 19. Estrutura XML representando o grafo de dependência final dos artefatos
Nesta figura está sendo descrito um artefato e suas relações de dependências, além
de informações extras sobre ele. Neste caso temos como exemplo o artefato chamado
ServicoPerfilOrgaoCa identificado pela tag <name>. Este artefato dentro da aplicação
está envolvido com controle de acesso aos métodos de serviços encapsulados por
componentes Enterprise Java Beans (EJB, 2011). Existem dois tipos de relações que
são descritas para este artefato. Um seria a lista de artefatos que ele depende definido
pela tag <outbound>. A outra lista seria dos artefatos que dependem dele definidos pela
tag <inbound>. Além disso, informações extras do próprio artefato como se ele é ou
não um ponto de implementação <implementationPoint> e a qual grupo ele pertence
abordado pela tag <group>.
Portanto nesta seção vimos o processo de construção do grafo final de
dependências dos componentes da aplicação. Todas as etapas estão ilustradas na figura
20:
53
Figura 20. Implementação da proposta
Primeiramente é processado o modelo do caso teste pela ferramenta MDArte.
Durante o processo de transformação de cada informação do modelo pelos cartuchos do
framework, são mapeadas todas as informações dos artefatos gerados. Neste caso são
mapeados quais elementos de modelo são responsáveis por sua geração além do grupo
ao qual eles pertencem, além disso se são sobrescritos a cada processo de
transformação. Durante o processo de transformação o mapeamento destas informações
são feitas através de um log.
Os critérios de classificação dos grupos dos artefatos é definido pelo próprio
arquiteto MDArte baseando em sua função dentro do contexto ao qual for executado.
54
Todas estas informações foram reunidas em uma matriz de rastreabilidade. Ela tem
como objetivo mapear a relação entre os artefatos gerados e os elementos de modelo,
caso existirem, responsáveis por sua geração.
Paralelamente o Dependency Finder analisa todo o código fonte da aplicação. A
saída é um grafo de dependência inicial pelo fato de conter todas as referências feitas
por todas as classes da aplicação. Estas referências podem ser diretas por meio de
relações verdadeiras com outros artefatos da própria aplicação. Elas também podem ser
referências indiretas por serem relações com artefatos que não pertencem aplicação, mas
pertencem a bibliotecas externas, por exemplo.
Neste momento precisamos cruzar as informações extraídas por ambas as
ferramentas com objetivo de apenas filtrar as informações que sejam relevantes para o
arquiteto. Este processo de filtragem da informação é ilustrado na figura pela interseção
dos conjuntos de artefatos.
Com o término do processo de filtragem é gerado um arquivo XML contendo
apenas as informações relevantes para o arquiteto. Estas informações compõem o grafo
de dependência final dos componentes. Como será descrito posteriormente na avaliação
experimental, este XML representa a base de dados no qual o arquiteto aplicará as
regras arquiteturais com a finalidade de detectar as violações de arquitetura.
55
6. Avaliação Experimental e detecção de violações de arquitetura
Neste capítulo abordaremos a avaliação experimental feita com o objetivo de
mapear relações de dependência que constituem violações de arquitetura. A nossa
avaliação foi aplicada a uma aplicação piloto responsável por gerenciar informações
sobre países. Esta aplicação segue o padrão MVC (MVC, 2010), apresentando módulos
compiláveis, cujo código é gerado pelo framework MDArte através do processamento
de seus modelos. A proposta inicial de detecção de violações de arquitetura que auxiliou
a construção do escopo desta tese está descrita em (SILVA et al. , 2010).
A seção 6.1 apresenta como o arquiteto interpreta a visualização do grafo de
dependência do artefato e dos elementos de modelo. Inicialmente é apresentada a
navegabilidade entre os elementos de modelo e suas associações, estas informações
foram obtidas durante o processo de transformação do modelo e mapeadas para um
grafo. A cada elemento navegável, uma lista dos artefatos gerados a partir dele é
apresentada, no qual a escolha de um exibe o mesmo como nó raiz do grafo de
dependência dos artefatos.
A seção 6.2 descreve como o arquiteto define as regras arquiteturais. Como as
regras evoluem juntamente com a aplicação, uma modelagem destas regras é
apresentada e a geração do arquivo XML correspondente. Os conceitos de diferentes
tipos de transições e os motivos dele serem aplicados também são ilustrados através de
um exemplo (subseção 6.2.1). A visualização e a análise do grafo de violações
56
arquiteturais são abordadas na subseção 6.2.2. Nesta parte é apresentado um novo
exemplo para nossa avaliação experimental com objetivo de mostrar extrações de
dependência em todas as camadas da aplicação para detectar violações de arquitetura.
6.1 Visualização do grafo de dependência
Durante a execução das regras de transformação do modelo pelos cartuchos do
MDArte, são mapeadas as relações entre os elementos de modelo a partir de seus
metafaçades. Este processo ocorre especificamente durante a construção da matriz de
rastreabilidade, explicada na seção 5.1. Ao identificar um determinado metafaçade, são
mapeadas suas relações com outros metafaçades construindo a navegabilidade entre os
elementos de modelo.
57
Figura 21. Navegabilidade entre os elementos de modelo
A figura 21 ilustra o processo de construção da navegabilidade entre os
elementos de modelo. Primeiramente na visão 1 é identificado o nome da aplicação e o
seu conjunto de casos de uso. Na visão 2, um determinado caso de uso da visão anterior
apresenta vários diagramas de atividade associados a ele. Por sua vez, o diagrama
selecionado na visão anterior contém classes de controle e outros elementos que o
descrevem na visão 3. Exemplos destes elementos são estados de ações e fluxos de
execução definidos pelo estado inicial, transições e estados finais. Na visão 4, a classe
de controle apresenta relações de dependência para classes de serviço da aplicação. A
classe de serviço ilustrada possui transições para classes de domínio na visão 5.
58
Contendo todo o grafo de dependência descrito em um arquivo XML, foi criada
uma ferramenta para navegar na estrutura da aplicação, visualizar este grafo de
dependência e mostrar as violações de arquitetura existentes.
Figura 22. Navegabilidade a partir de um caso de uso até os elementos de seu diagrama de atividade
A figura 22 acima ilustra a visão estrutural de um módulo do caso teste
apresentado. Nele os nós descrevem a estrutura da aplicação e as setas indicam o que
elas contêm. No caso acima o nó SuportePais é nome da aplicação. Ela é composta de
vários casos de uso, dentro os quais ilustrado pelo nó azul ConsultarPais. Este caso de
uso contém um diagrama de atividades chamado ConsultarPaisDA, representado pelo
nó na cor verde. Neste diagrama de atividades foram modelados todos os seus
elementos como nós roxos. A classe de controle CosnultaPaisControle com a função de
59
ser uma action na camada de visão e os elementos do diagrama como ações (Resultado
da consulta Pais) e suas transições (Excluir) compondo a navegabilidade pelas páginas
web.
A partir desta visão estrutural o arquiteto pode navegar entre seus componentes na
árvore e focar em um artefato específico, clicando neste artefato é mostrado em até três
níveis na árvore de dependência as relações que existem entre os artefatos associados a
ele.
Além das relações de dependências descritas em nível de artefato, podemos obter o
grafo entre os elementos de modelo responsáveis pela geração de um determinado
artefato e suas dependências. Conforme discutido anteriormente, o grafo de elementos
de modelo é utilizado para o arquiteto poder avaliar as regras de transformação descritas
nos cartuchos do MDArte.
60
Figura 23. Grafo de dependência dos artefatos e grafo de dependência dos elementos de modelo correspondentes
Os grafos ilustrados na figura 23 acima foram extraídos do caso teste para
exemplificar as informações que o arquiteto pode analisar a partir desta visualização.
No lado esquerdo da figura encontramos o grafo de dependência dos artefatos em
diferentes níveis a partir do nodo raiz ConsultaPaisControleImpl. Este artefato pertence
à camada de visão da aplicação com a função de receber os dados vindos dos
formulários do sistema. Além deste artefato, ConsultaPaisControle e
ConsultaPaisForm são classificados pelo arquiteto como pertencente ao grupo visão, de
acordo com sua função dentro do contexto da aplicação. Ele apresenta uma relação de
dependência direta com os seguintes artefatos: ConsultaPaisForm responsável por
61
carregar os dados descritos em um formulário, ConsultaPaisControle por herdar seus
métodos.
Além deles, existe uma relação direta com os artefatos Action e PaisImpl. Ambos
pertencem à camada de domínio e estão relacionados à camada de persistência dos
dados. Devido a este fato foram classificados como grupo domínio.
Por último, o artefato PaisHandlerPBI é responsável pelo controle de acesso aos
métodos dos componentes de serviço. Ele depende diretamente da interface
PaisHandlerBI que descreve a assinatura dos métodos que são implementados pelo
componente de serviço PaisHandlerBean. Este por sua vez, possui métodos que são
sobrescritos pela classe filha PasiHandlerBeanImpl. Todos pertencem ao grupo serviço.
A visualização do grafo de dependência dos artefatos permite verificar as relações
existentes entre componentes do mesmo grupo e de grupos diferentes.
As relações previstas durante o processo de construção da arquitetura pelo
framework serão constantes em cada processo de transformação do modelo. Os
artefatos que não são pontos de implementação apresentam suas dependências de
acordo com a definição da arquitetura em camadas, no qual cada cartucho é responsável
por sua geração.
As violações de arquitetura poderão estar presentes nos artefatos que são pontos de
implementação. Por terem sido criados para implementar um comportamento
indescritível no modelo, o arquiteto pode verificar as relações de dependência que
comprometem a arquitetura a partir das referências entre estes componentes e seus
grupos. Existe também a possibilidade remota de um desenvolvedor alterar naquele
estado do sistema, um artefato que será gerado novamente em uma transformação futura
do modelo, de qualquer forma a violação também será detectada nas transições deste
artefato.
62
O artefato apresenta a característica de ter sido gerado a partir de informações
descritas no modelo ou ser gerado independente dele. Durante o processo de
transformação, um ou mais elementos de modelo podem ser responsáveis pela geração
de um determinado artefato. No entanto, para atender alguns requisitos da aplicação
alguns podem ter sido criados manualmente pelos desenvolvedores ou como recursos de
um cartucho específico. Nestes casos em relação a sua geração, eles independem das
informações descritas no modelo.
A partir do grafo de dependência dos artefatos podemos concluir como deve ser
construído o grafo de dependência dos elementos de modelo correspondentes. Para a
construção de tal grafo começamos com a raiz, ou seja, os elementos de modelo e suas
dependências são obtidos a partir das relações e informações presentes em cada artefato.
Primeiramente é analisada a raiz do grafo dos artefatos, no caso
ConsultaPaisControleImpl. O grupo ao qual a raiz pertence é a visão e o elemento
responsável pela sua geração é chamado ConsultaPaisControle, associado ao diagrama
de atividades ConsultarPaisDA. Portanto a raiz do grafo de modelos será o elemento
classe de controle ConsultaPaisControle. O artefato ConsultaPaisControleImpl herda
da super classe ConsultaPaisControle, porém esta também é gerada a partir do mesmo
elemento de modelo. Neste caso o mesmo elemento é responsável pela geração destes
dois artefatos.
Continuando a análise de dependência a partir da raiz, o artefato
ConsultaPaisControleImpl apresenta uma dependência com a classe
ConsultaPaisForm. Esta ultima é gerada a partir do caso de uso ConsultaPais com
estereótipo <<Use Case>>. Assim a primeira dependência descrita no grafo de
modelos é entre o elemento de controle e o caso de uso, todos pertencentes ao grupo
visão.
63
A relação de dependência a nível de modelo entre o elemento de controle e o
elemento PaisVO com estereótipo <<Value Object>> foi obtida porque ele é
responsável pela geração da classe PaisVO. Neste caso ambos pertencem ao grupo
comum e são identificados com a respectiva cor.
A relação de dependência entre a raiz e o elemento Pais com esteriótipo
<<Entity>> foi obtido porque este elemento é responsável pela geração do artefato
Pais pertencente ao grupo de domínio. Neste caso o nodo também é identificado na cor
vermelha.
A coloração verde no elemento PaisHandler com estereótipo <<Service>> é
devido a ele ser responsável pela geração de todos os artefatos pertencentes a camada de
serviço do sistema.
A partir do grafo de dependência dos elementos de modelo pode ser analisado o
relacionamento entre eles com objetivo de validar a arquitetura da aplicação. Este grafo
permite também verificar se os mapeamentos entre os elementos e seus respectivos
artefatos estão corretos. Permite ao arquiteto verificar se as regras do processo de
transformação do modelo, definidas em cada cartucho, estão sendo definidas de forma
correta, ou seja, podem ser detectados erros na arquitetura do framework MDA
responsável pela geração da aplicação.
A figura 24 ilustra um erro descrito na arquitetura do framework, ou seja, uma
regra incorreta definida dentro do cartucho responsável pela camada de persistência da
aplicação. Este erro foi descoberto a partir da analise de dependência entre dois
elementos de modelo de grupos diferentes.
64
Figura 24. Identificação da regra incorreta de transformação do modelo
Voltamos ao exemplo visualizado anteriormente extraído de nosso caso teste. O
arquiteto podia verificar no grafo de dependência dos elementos de modelo que a
relação entre o elemento ConsultaPaisControle e o elemento ContextoCa <<Entity >>
não era prevista. Existia uma relação de dependência entre um elemento do grupo de
visão com um elemento do grupo de domínio que se encontrava fora daquele contexto.
Inicialmente foi analisado o motivo de existir tal elemento do grupo de domínio.
Sua presença no grafo é devido a ele ser o responsável pela geração do artefato Action.
Este artefato era referenciado pelo artefato ConsultaPaisControleImpl conforme
ilustrado em dependência na cor vermelha na figura.
65
Identificado que o artefato Action foi gerado pelo elemento de modelo ContextoCA,
foi verificada a inconsistência no processo de transformação dos elementos de modelo
pelo cartucho Hibernante, responsável por gerar os artefatos pertencentes ao grupo de
domínio.
O problema nesta regra de transformação está sendo mostrado pela figura 25
abaixo:
Figura 25. Regra incorreta de transformação do modelo
O artefato Action apresenta as seguintes características identificadas em seu
template. Ele pertence ao grupo domínio por possuir função na camada de persistência
da aplicação, não é um ponto de implementação e estava descrito para ser gerado caso
existisse no mínimo um elemento de modelo com estereótipo <<Entity>>. Ele não
devia ser gerado por um elemento especifico, deveria ser gerado caso existisse pelo
menos um elemento de modelo com estereótipo <<Entity>>. Entretanto ele estava
sendo sobrescrito cada vez que um elemento fosse encontrado no modelo. Assim de
66
acordo com a figura, este artefato foi gerado quatro vezes, um para cada elemento
<<Entity>> encontrado no qual a ultima relação de geração ocorreu devido ao
elemento ConextoCa.
Portanto este fato descreve um erro de mapeamento lógico em uma das regras de
transformação executado pelo cartucho Hibernate (HIBERNATE, 2010). Somente foi
possível detectar esta inconsistência devida à análise destes elementos no grafo de
dependência. Este mapeamento de incorreções nas regras de transformação, descritas
nos cartuchos, pode ser aplicada na abordagem de geração parcial ou total do código em
um ambiente MDA.
A solução deste problema de transformação está descrito na figura 26 abaixo:
Figura 26. Descrição da solução da regra incorreta de transformação do modelo
A regra de geração do artefato Action foi alterada. A condição para que não exista a
relação entre o elemento de modelo ConsultPaisControle e ContrextoCA é que tal
artefato somente deve ser gerado uma única vez caso exista no mínimo um elemento de
67
modelo com estereótipo <<Entity>>. O artefato Action somente pode ser gerado caso
seja modelado pelo menos um dos quatro elementos na figura.
Portanto esta solução permite que a relação entre ambos os elementos de modelo
não exista quando obtida a partir da relação dos seus respectivos artefatos. A
identificação das violações a partir das regras de transformação do modelo é uma
contribuição importante desta dissertação. Estas falhas, presentes nas regras de um
framework MDA, somente são identificadas cruzando as informações do grafo de
dependência dos elementos de modelo com a matriz de rastreabilidade mapeando os
artefatos gerados a partir de cada elemento de modelo.
Nesta seção vimos como são elaborados os passos que constituem a avaliação
experimental desta dissertação. De uma forma geral ele é ilustrado na figura 27 abaixo
relatando todos os passos do caso teste:
Figura 27. O caso teste
68
Primeiramente o Dependency Finder é utilizado para obter um grafo de
dependência inicial a partir do código fonte final da aplicação. Portanto, para melhorar a
análise das dependências do projeto, nós criamos uma matriz de rastreabilidade que
mapeia os elementos do modelo para um ou mais artefatos do código fonte final. A
partir da matriz construída, são cortadas todas as informações desnecessárias do grafo
de dependência inicial através do cruzamento com as informações da matriz de
rastreabilidade. Como resultado, um grafo de dependência representado em UML, que
contém informações necessárias apenas, é gerado a partir das regras arquiteturais
estabelecidas, aplicando tais regras neste modelo com a finalidade de identificar
violações arquiteturais.
6.2 Aplicando a proposta para a visualização de violações arquiteturais
O framework MDArte segue a abordagem de geração parcial do código, ou seja,
pontos de implementação são utilizados para expressar comportamentos da aplicação
não descritos em modelos. Embora estas customizações sejam a chave para desenvolver
sistemas por razões abordadas no capítulo 2, eles também podem representar uma
chance para violações de arquitetura desde que não estejam vinculadas as regras
arquiteturais configuradas nas transformações. Na figura 28 que ilustra nossa aplicação
para identificar e visualizar violações arquiteturais é apresentado este conceito.
69
Figura 28. A proposta para visualização de violações de arquitetura
No MDArte, as transformações do modelo para código são executadas por regras
definidas em seus cartuchos. Em relação à construção destes cartuchos, o arquiteto tem
a tarefa de definir os artefatos (em uma plataforma específica) que cada elemento de
modelo irá representar no processo de criação deles. Ele também é responsável por
estabelecer um conjunto de grupos (de artefatos) e relacioná-los aos artefatos gerados de
acordo com suas necessidades. Desta relação, é possível descobrir em qual grupo cada
artefato pertence independente de quando as transformações ocorram. Esta informação é
extraída durante a construção da matriz de rastreabilidade mencionada no capítulo
anterior.
Enquanto são definidos os grupos e seus artefatos relacionados, o arquiteto de
software deve também listar possíveis transições entre os grupos. Como mencionado
antes, o uso de customizações via pontos de implementação representam um risco
70
adicional para a introdução das violações de arquitetura. O arquiteto deve estar ciente
das mudanças que estas customizações podem gerar e como corrigi-las se necessário.
Eles podem criar um novo conjunto de regras, se necessário, na forma de grupos ou
subgrupos e suas transições. No MDArte todas estas regras são descritas em modelos
independente de plataforma podendo ser mapeadas para uma notação XML. De acordo
com arquiteto de software, as regras arquiteturais definem grupos, subgrupos e as
transições permitidas entre eles. Esta informação é utilizada para identificar violações
na arquitetura da aplicação.
Com o objetivo de alterar ou criar alguma regra arquitetural, o arquiteto deve
primeiro checar o estado atual do sistema para procurar alguma violação arquitetural
que possa existir. Nossa proposta é ajudá-lo em tarefas específicas. Nós apresentamos
ao arquiteto a visualização de uma violação na forma de um grafo. Para construir este
grafo é necessário obter todos os artefatos, suas dependências, seus grupos relacionados
e suas transições permitidas e não permitidas. Por transição entenda-se qualquer tipo de
dependência entre os artefatos. Do grafo de dependência cuja geração foi explicada no
capítulo 5, nós resgatamos todos os artefatos gerados quanto todas suas transições,
sendo elas permitidas ou não. Das regras arquiteturais descritas no PIM nós podemos
identificar os grupos relacionados de cada artefato e as transições permitidas.
Considerando estas transições permitidas e rearranjando o grafo para a inclusão dos
grupos, nós temos uma versão final do grafo para visualizar todas as violações de
arquitetura do sistema. Nosso grafo foi gerado utilizando a notação XML e importado
para uma ferramenta construída durante a tese para melhor visualização.
71
Figura 29. Conceitos sobre violações arquiteturais
A figura 29 mostra um grafo de violações conceitual para exemplificar o que foi
explicado anteriormente nesta seção. Este grafo apresenta três grupos identificados por
diferentes nomes determinados pelo arquiteto. Os nós são artefatos de um grupo
específico e as setas representam violações de arquitetura. A origem da seta é o artefato
que comete a violação. Todos os artefatos do grupo 1 cometem alguma violação porque
eles dependem do artefato do grupo 2. Este por sua vez também comete violações
arquiteturais por referenciar artefatos pertencentes ao grupo 3.
72
6.2.1 Criação das regras arquiteturais
Conforme explicado anteriormente, as regras arquiteturais definem as transições
entre artefatos das diversas camadas da aplicação. Estas regras envolvem diferentes
tipos de relações entre artefatos, subgrupos, grupos pré-estabelecidos ou novos grupos.
Para este caso teste apresentamos uma documentação dos grupos, subgrupos e
novos grupos. As definições dos grupos e das regras arquiteturais para seus elementos
estão ilustradas no diagrama de componentes da figura 30 abaixo. Nela observamos que
elementos do grupo visão podem ter relações de dependência com os elementos do
grupo serviço e com os elementos do grupo comum.
Figura 30. Modelagem das regras arquiteturais entre grupos
.
A descrição das regras arquiteturais em um modelo pode variar ao longo do ciclo
de vida do projeto, portanto a cada liberação de uma versão do sistema, este modelo
73
pode sofrer alterações para a aplicação aderir à nova arquitetura estabelecida. O
arquiteto após examinar as violações de arquitetura da versão atual da aplicação e
solucionar tais falhas, pode alterar as regras arquiteturais no modelo e gerar novamente
a aplicação a partir dos modelos. Uma nova análise da arquitetura é feita nesta nova
versão do sistema, o sincronismo entre as regras modeladas e geradas pelas
transformações dos cartuchos é garantido.
No entanto nem todas as regras arquiteturais podem ser expressas através do
modelo, como por exemplo, definir relações específicas de dependência para um
determinado artefato. O conceito de subgrupo surgiu pela necessidade do arquiteto em
definir para conjuntos específicos de artefatos algumas transições restritas. Nestes casos
o conjunto ao qual um ou mais artefatos referenciam, pode ser formado na verdade por
artefatos de diferentes grupos. Estes tipos de conjunto não são estabelecidos durante as
transformações do modelo pelos cartuchos do MDArte pro envolverem artefatos
específicos.
O processamento do modelo adotado em nosso caso teste durante a etapa de
transformação resulta em um arquivo XML que descreve as regras arquiteturais do
sistema gerado. A estrutura do XML foi baseada na relação entre conjuntos, sendo
exemplificada na figura 31 abaixo:
74
Figura 31. Estrutura XML de definição das regras arquiteturais
Este arquivo XML define a arquitetura a ser seguida pelos desenvolvedores da
equipe. O arquiteto ao elaborar os artefatos que serão gerados pelo framework e suas
dependências, modela as novas regras arquiteturais. Durante o ciclo de vida do projeto,
tais regras devem ser criadas ou alteradas para que aplicação continue aderindo à
arquitetura estabelecida. Cada regra acaba definindo um subgrupo que descreve esta
relação de dependência.
O elemento <subgroup> com os atributos id e name identificam esta nova regra.
Ela é baseada na relação entre aqueles que dependem e aqueles que são dependidos. A
tag <Elements> definem aqueles que dependem. Ambos os elementos podem ser um
determinado artefato, um conjunto de artefatos ou até pacotes definidos pela tag
<package>. Além destes, subgrupos ou grupo também podem ser descritos nestes
elementos.
75
Pelo outro lado da relação, a parte envolvendo aqueles que são dependidos por
alguém é constituído pela tag <relations>. Os elementos que compõe esta parte também
podem ser um ou mais artefatos, pacotes, subgrupos ou grupos.
A figura 32 ilustra um exemplo de como o arquiteto aplicaria a criação de uma
regra para o módulo de consulta de países. A definição das transições arquiteturais é
gerada em um XML.
Figura 32. Estrutura gerada a partir da transformação do modelo de regras arquiteturais do módulo de consulta de países.
A raiz deste nosso grafo de componentes é o artefato ConsultaPaisContrleImpl
pertencente ao grupo visão. Este grupo é gerado pelas regras de transformação descritas
no cartucho bpm4Struts. Ele é responsável por gerar todos os artefatos de configuração
do Struts e as telas jsp. O desenvolvedor só precisará programar (elaborar) os métodos
dos artefatos relativos à interceptação dos eventos da tela.
Dentro do módulo de consulta de países de nosso caso teste, ele apresenta a função
de interceptar eventos vindos da interface como, por exemplo, receber os dados do
76
formulário para poder tratá-los. O pacote consultaPais contém todos os artefatos
pertencentes ao grupo de visão que estão associados a consulta de países, inclusive o
ConsultaPaisContrleImpl.
O artefato PaisVO pertence ao grupo comum. Este grupo possui como integrantes
todos que apresentam a finalidade de trafegar dados entre camadas. Portanto PaisVO é
responsável por trafegar os dados de um país pelas camadas de serviço, domínio e visão
do sistema.
O artefato PaisHandlerPBI pertence ao grupo de serviço. Este grupo foi definido
pelo arquiteto pelo fato de compor a camada de serviço da aplicação. Toda a lógica de
negócio da aplicação está implementada em seus componentes. O cartucho EJB gera
todos os artefatos de configuração dos session beans, além de todas as interfaces
necessárias. O desenvolvedor só precisará implementar (elaborar) os métodos do Bean.
A função do PaisHandlerPBI é fornecer as regras de negócio para acessar os dados
referentes a um determinado país.
Definidas as razões de classificação para cada grupo, serão analisadas as regras
arquiteturais descritas no XML. A relação de dependência entre o artefato
ConsultaPaisControleImpl e PaisHandlerPBI define que somente tal componente de
serviço possui as regras de consulta de países, portanto somente ele deve ser chamado
por este artefato do grupo visão.
Em relação ao grupo comum, a transição específica entre a raiz e o artefato PaisVO
define que somente ele pode transportar os dados de um país para ele.
A transição de ConsultaPaisControleImpl para o subgrupo do grupo visão,
composto por um conjunto de artefatos do módulo de consultar países, descreve a
relação lógica entre eles.
77
6.2.2 Visualização do grafo de violações arquiteturais
Nesta seção será apresentado outro exemplo de nossa avaliação experimental
que descreve o processo de elaboração e visualização das violações de arquitetura. Ele
se difere dos demais por envolver artefatos pertencentes à interface visual do sistema,
mostrando que as regras arquiteturais podem ser aplicadas a todas as camadas da
aplicação.
Neste momento o arquiteto possui informações do sistema resgatadas diretamente
do código fonte ou dos modelos. Através do processo de engenharia reversa, da
construção da matriz de rastreabilidade, da classificação dos artefatos em grupos e da
aplicação de filtros obtemos um grafo de dependência dos artefatos e dos elementos de
modelo responsáveis por suas gerações.
A partir destes grafos, o arquiteto aplica as regras arquiteturais modeladas em um
diagrama de componentes UML com objetivo de identificar as violações de arquitetura.
Estas violações são representadas também por um grafo de dependência de artefatos ou
dos elementos de modelo associados. Cada nodo do grafo é identificado com uma cor
que representa o grupo ao qual pertence.
De um modo geral, o arquiteto ao modelar as regras arquiteturais define os critérios
de transições entre artefatos de diferentes grupos. A figura 33 abaixo representa o
diagrama de componentes modelado:
78
Figura 33. Modelagem das regras arquiteturais entre grupos
Ao definir os grupos gerais presentes em cada camada da aplicação, o arquiteto
estabelece esta arquitetura através das regras de transformação pelos cartuchos do
MDArte. Portanto, compreendemos que o grupo visão deva depender diretamente de
artefatos pertencentes ao grupo comum ou ao grupo de serviço. Este por sua vez deve
apresentar transições com o grupo comum e com integrantes do grupo de domínio.
Estes últimos somente podem referenciar artefatos do grupo comum.
Existe a necessidade de definir regras arquiteturais específicas para detreminados
elementos de um grupo. Uma solução de modelo destas regras seria o diagrama de
componentes. A figura 34 ilustra uma regra específica no qual qualquer JSP de nossa
aplicação teste poderá referenciar apenas uma artefato específico responsável pelo
layout das telas e qualquer artefato pertencente ao grupo comum.
79
Figura 34. Modelagem de uma regra arquitetural específica para um subgrupo
A partir desta regra arquitetural especificada em um diagrama de componentes
UML, o XML descrito na figura 35 é gerado.
80
Figura 35. Descrição de uma regra arquitetural específica para um subgrupo
Nela descremos a definição de uma regra arquitetural para o artefato do grupo de
visão resultado_da_consulta_pais_actions_jsp. Este componente (nodo raiz),
classificado com grupo de visão, é uma JSP (JSP, 2011) pertencente ao módulo de
consulta de países. A tecnologia JSP contém conteúdo estático e conteúdo dinâmico,
sua geração pelo cartucho Struts desonera o desenvolvedor de se preocupar com o
design das páginas da nossa aplicação. Como as JSPs podem ser customizadas pelos
desenvolvedores, ou seja, além de conteúdo HTML (HTML, 2011) pode conter
conteúdo Java também, durante o processo de engenharia reversa para recuperar
informações diretas do código fonte, ele também é analisado pelo extrator de modo a
obter suas dependências.
O nodo raiz depende do artefato LayoutConfiguration, ilustrando uma transição
específica entre dois componentes. Este artefato apresenta funções utilitárias para serem
81
aplicadas ao módulo de layout do sistema, além dele, somente artefatos transportadores
de dados presentes no pacote tutorial.vo podem ser dependidos por ele. Este subgrupo
segue o padrão de projeto Transfer Object, no qual um objeto é responsável por
encapsular os dados e navegar entre as camadas levando estes dados.
Com as regras arquiteturais definidas, o próximo passo é aplicá-las ao grafo de
dependência dos artefatos que se deseja analisar durante este processo de
automatização. A figura 33 abaixo ilustra a visualização de uma parte do grafo de
dependência no qual temos o artefato resultado_da_consulta_pais_Actions_js como
nodo raiz. Ele apresenta um conjunto de transições para artefatos do próprio grupo
(camada de vista), para artefatos do grupo comum (camada de domínio). Existem
transições também para componentes do grupo de domínio (camada de domínio) e para
componentes do grupo de serviço (camada de domínio).
Visualizando este grafo, identificamos a transição entre a raiz e o artefato
LayoutConfiguration do próprio grupo. Em relação ao grupo comum, ele identifica
relações de dependência entre ele e os artefatos PaisVO e UFVO. Ele também depende
do artefato PaisHandlerPBI pertencente ao grupo de serviço. Os artefatos Action e Pais,
do grupo de domínio, também são referenciados por ele.
82
Figura 36. Visualização do grafo de dependência
Cruzando as relações de dependência reais descritas no grafo anterior com as
transições permitidas pelas regras arquiteturais, são obtidas as violações de arquitetura
do sistema, ou seja, são identificadas as transições que não deveriam existir.
A identificação das transições que representam violações de arquitetura começa na
raiz cuja regra foi identificada. A regra arquitetural para o nó
resultado_da_consulta_pais_actions_jsp está descrito na figura 34. A relação dele para
o outro artefato do mesmo grupo está descrito na regra, ou seja, não constitui uma
violação. Analisando o grupo de domínio, as transições dele para os artefatos daquele
grupo também não constituem uma falha por estarem definidos na regra arquitetural.
Entretanto, em relação aos grupos de serviço e domínio, as violações estão
presentes. Ao definir a regra de arquitetura para JSP, o objetivo do arquiteto era apenas
83
permitir dependências para outro artefato ou para aqueles que seguem o padrão de
projeto Transfer Object que integram o subgrupo do grupo comum.
A partir destas informações concluímos que as relação de dependência dele para o
artefato PaisHandlerPBI constitui uma violação arquitetural. As transições para os
artefatos Action e Pais do grupo de domínio também constituem violações arquiteturais.
A análise deste grafo ilustrado na figura 34 abaixo permite ao arquiteto discutir com os
desenvolvedores as razões que levaram a customizar estes pontos de implementação.
Figura 37. Visualização das violações arquiteturais em um grafo de dependência de artefatos
Após estas reuniões, estes pontos de implementação são alterados de modo a
obter as regras arquiteturais modeladas da versão atual do sistema. Com a evolução
constante do projeto, novos requisitos devem ser atendidos, o que resulta em novas
84
alterações dos modelos e a evolução da arquitetura do próprio MDArte para a geração
dos novos artefatos. No entanto, as customizações destes pontos de implementação
(antigos ou novos) podem apresentar violações de arquitetura.
A detecção destas novas violações é realizada a partir da extração das informações
presentes no código fonte e nos elementos de modelo. Portanto é um processo que pode
ser agendado durante o ciclo de vida do projeto, por estarem associadas diretamente as
transformações dos modelos atuais e do código vigente gerado no estado atual que o
sistema se encontra.
85
7. Conclusão
Esta dissertação apresenta uma proposta para automatizar a detecção de violações
de arquitetura em um cenário de desenvolvimento orientado a modelo (MDA). Nela é
descrita como manter um diagrama de dependência dos artefatos com informações
obtidas a partir dos modelos e dos pontos de implementação. A partir destas
informações é apresentado como derivar o grafo de dependência dos elementos de
modelo correspondentes, utilizando uma matriz de rastreabilidade que vincula o artefato
ao elemento, também construída em tempo de transformação do modelo. Dada uma
especificação de arquitetura, através de critérios de classificação dos artefatos em
grupos e da modelagem de regras arquiteturais, nossa proposta permite a automatização
da identificação de violações arquiteturais.
As regras arquiteturais também são expressas através de modelos e descritas em
uma linguagem independente de plataforma. Dessa forma, artefatos que especificam as
regras arquiteturais podem ser gerados usando a própria abordagem MDA. Assim, as
regras arquiteturais são aplicadas ao grafo de elementos de modelo, com objetivo de
construir um grafo de violações arquiteturais.
Na abordagem MDA de geração completa a proposta identifica violações oriundas
de evoluções das transformações de modelo. Na abordagem de geração parcial, além de
monitorar violações originadas em evoluções de transformações, a proposta também
valida se o código inserido em pontos de implementação obedece às regras arquiteturais
pré-definidas.
A identificação das violações a partir das regras de transformação do modelo é uma
contribuição importante desta dissertação. Estas falhas, presentes nas regras de um
framework MDA, somente são identificadas caso o grafo de dependência dos elementos
86
de modelo seja gerado, assim como a matriz de rastreabilidade mapeando os artefatos
gerados a partir de cada elemento de modelo.
Este processo de identificação das violações de arquitetura pode ser agendado para
ser executado em estados futuros do sistema uma vez que o mesmo sempre garante o
sincronismo entre os modelos e a aplicação.
A montagem do grafo de dependência é uma contribuição extremamente importante
para os trabalhos futuros. A partir dela, poderemos realizar o processo de análise de
impacto e de contagem de pontos de função. Outra contribuição é realizar a medição de
acoplamentos dos módulos de uma aplicação a partir de métricas extraídas diretamente
das relações de dependência entre os artefatos que compõem cada módulo. Este grafo
também fornece subsídios para adaptações da modernização da aplicação, além de
permitir validar ou avaliar a aderência da aplicação à arquitetura estabelecida.
87
Referências bibliográficas ADM, 2010, “ARCHITECTURE-DRIVEN MODERNIZATION”. Disponível em
http://adm.omg.org/. Acesso em outubro de 2010.
ANDROMDA, 2010, “ANDROMDA”. Disponível em http://www.andromda.org.
Acesso em agosto de 2010.
ALUR, D., MALKS, D., CRUPI, J., 2001, Core J2EE Patterns: Best Practices and
Design Strategies, 2 Ed, Prentice Hall.
AL-EKRAM, R., KONTOGIANNIS, K., 2005, “An XML-based framework for
language neutral program representation and generic analysis”. In: CSMR ’05:
Proceedings of the Ninth European Conference on Software Maintenance and
Reengineering, pp 42–51, IEEE Computer Society.
ARNOLD, R.S., BOHNER, S.A., 1993, “Impact Analysis—Towards a Framework for
Comparison,” In: Proceedings of International Conference on Software Maintenance,
pp. 292–301.
BIGGERSTAFF, T., 1989, Design Recovery for Maintenance and Reuse,
IEEE Computer, July.
BOURQUN F., KELLER, R. K., 2007, “High-impact Refactoring Based on
Architecture Violations”. In: CSMR '07 Proceedings of the 11th European Conference
on Software Maintenance and Reengineering, pp 149 – 158, IEEE Computer Society.
88
BORK, M., GEIGER L., SCHNEIDER, C., ZUNDORF, A., 2008, “Towards Roundtrip
Engineering - A Template-Based Reverse Engineering Approach”. In: European
Conference on Model Driven Architecture - Foundations and Applications –
ECMDAFA, pp 33-47.
BORONAT A., CARSI J. A., RAMOS I., 2005, “Automatic reengineering in MDA
using rewriting logic as transformation engine”. In: CSMR ’05: Proceedings of the
Ninth European Conference on Software Maintenance and Reengineering, pp 228–231,
Washington, DC, USA. IEEE Computer Society.
BRIAND, L., LABICHE, Y., MIAO, Y., 2003 , “Towards the Reverse Engineering of
UML Sequence Diagrams”. In: WCRE '03 Proceedings of the 10th Working Conference
on Reverse Engineering, pp 57, IEEE Computer Society.
BROOKS, F.P. JR., 1972, Mythical Man-Month The: Essays on Software Engineering.
1 Ed, Addison-Wesley.
CEPA, V., MEZINI, M., 2006, “Language support for model-driven software
development”. Journal Science of Computer Programming, v. 73 , n. 1, pp 13-25.
CHIKOFSKY, E., II, J.C, 1990, “Reverse Engineering and Design Recovery: A
Taxonomy,” IEEE Software, v. 7, n. 1, pp.13–17, Jan.
89
CHOWDHURY, A., MEYERS, S., 1993, “Facilitating software maintenance by
automated detection of constraint violations”. In: Proceedings of the 1993 Conference
on Software Maintenance, pp 262-71, Quebec, Canada, 27-30 September. IEEE.
CIUPKE, O.,1999, “Automatic Detection of Design Problems in Object-Oriented
Reengineering”. In: Technology of Object- Oriented Languages and Systems - TOOLS
30, IEEE Computer Press.
CZARNECKI, K., HELSEN, S., 2003, “Classification of model transformation
approaches”. OOPSLA2003 Workshop on Generative Techniques in the Context of
MDA, Anaheim, CA, USA.
DEPENDENCY FINDER, 2011, “DEPENDENCY FINDER”. Disponível em
http://depfind.sourceforge.net/ . Acesso em março de 2011.
DOYLE D., 2005 , Transforming proprietary domain-specific modeling languages to
modeldriven architectures. Master dissertation , Delft University of Technology,
Netherlands.
EDWARDS, S. H., SHAKIR, G., SITARAMAN, M., WEIDE, B. W.,
HOLLINGSWORTH, J., 1998, “A framework for detecting interface violations in
component-based software”. In: Proceedings of the Fifth International Conference on
Software Reuse, pp 46, IEEE Computer Society Press, June.
90
EJB, 2011, “ENTERPRISE JAVA BEANS”. Disponível em
http://www.oracle.com/technetwork/java/javaee/ejb/index.html . Acesso em março de
2011.
EMF, 2010, “Eclipse Modeling Framework”. Disponível em
http://www.eclipse.org/emf/. Acesso em outubro de 2010.
FAVRE, J.-M., NGUYEN, T., 2005, “Towards a megamodel to model software
evolution through transformations”. Electronic Notes in Theoretical Computer Science,
v127, n. 3, pp 59–74.
FENTON, N. E., PFLEEGER, S. L., 1997, Software Metrics. A Rigorous and Pratical
Approach, 2 Ed, PWS Publishing Company.
FERENC, R., MAGYAR F., BESZEDES A., KISS A., TARKIAINEN M, 2002,
“Columbus - Reverse Engineering Tool and Schema for C++”. In: ICSM '02
Proceedings of the International Conference on Software Maintenance, v. 0, pp 172,
IEEE Computer Society.
FOWLER M., 1999, Refactoring: Improving the Design of Existing Code, Addison-
Wisley.
FYSON, M.J., BOLDYREFF, C., 1998, “Using Application Understanding to Support
Impact Analysis,” Jornal of Software Maintenance - Research and Practice, v. 10,
pp. 93–110.
91
GANNOD, G., CAREY M., 2004, “Evolution of java programs to a model-driven
environment using EMF”. In: Proceedings EDOC Workshop on Model-Driven
Evolution of Legacy Systems (MELS). IEEE Computer Society Digital Library.
GARLAN, D., PERRY, D., 1994, “Software Architecture: Practice, Potential and
Pitfalls”. In: Proceedings of 16th International Conference on Software Engineering, pp
363 - 364, Sorrento, Italy, May.
GARLAN, D., SHAW, M., 1993, “An Introduction to Software Architecture”. In:
Advances in Software Engineering and Knowledge Engineering, vol. 2, Software
Engineering and Knowledge Engineering, World Scientific Publishing Company,
pp 1 - 39.
GEN, M.; CHENG, R., 2002 , Generic Algorithms and Engineering Optimisation.
1 Ed, New York, Wiley .
GUTTAG, J. V., HORNING, J. J., WING, J. M., 1985, “The Larch Family of
Specification Languages”. Journal IEEE Software, v. 2 n.5, pp 24 -36.
HIBERNATE , 2010. “HIBERNATE”. Disponível em http://www.hibernate.org.
Acesso em setembro de 2010.
HTML, 2011, “HYPERTEXT MARKUP LANGUAGE”. Disponível em
http://www.w3.org/html/. Acesso em março de 2011.
92
JAVA, 2010, “JAVA TECHONOLOGY SUN MICROSYSTEMS”. Disponível em
http://java.sun.com. Acesso em setembro 2010.
JONES, A. K., 1994 , “The Maturing of Software Architecture”. Software Engineering
Symposium, Software Engineering Institute, Pittsburgh, Pa., August.
JSP, 2011, “JAVA SERVER PAGES”. Disponível em
http://www.oracle.com/technetwork/java/javaee/jsp/index.html . Acesso em março de
2011.
J2EE, 2011, “JAVA 2 ENTERPRISE EDITION”. Disponível em
http://java.sun.com/j2ee/overview.html. Acesso em março de 2011.
KAASTRA, M., KAPSER, C., 2003, Toward a semantically complete java fact
extractor. Department of Computer Science, University of Waterloo, April.
KERIEVSKY, J., 2004, Refactoring to Patterns, Addison-Wesley.
KLEPPE, A., WARMER, J. AND BAST, W., 2002, MDA Explained: The Model
Driven Architecture: Practice and Promisse. Addison-Wesley.
KONCLIN, J., BERGEN M., 1988, “Gibis: A Hypertext Tool for Exploratory Policy
Discussion.”. ACM Trans. Office Information Systems, v. 6, n. 4, pp. 303–331, October.
93
KORSHUNOVA, E., PETKOVIC, M., VAN DEN BRAND, M. G. J., MOUSAVI, M.
R., 2006, “CPP2XMI: Reverse Engineering of UML Class, Sequence, and Activity
Diagrams from C++ Source Code”. In: 13th Working Conference on Reverse
Engineering, pp 297–298. IEEE Computer Society.
KURTEV I., EZEVIN J. B´, AKSIT, M., 2002, “Technological spaces: An initial
appraisal”. In: CoopIS, DOA 2002 Federated Conferences, pp. 1-6, Springer-Verlag.
LISKOV B., GUTTAG, J., 1986, .Abstraction and Specification in Program
Development.McGraw-Hill.
MANSUROV, N., CAMPARA, D., 2005, “Managed architecture of existing code as a
practical transition towards MDA”. In: UML Modeling Languages and Applications:
<<UML>> 2004 Satellite Activities, v. 3297, Lecture Notes in Computer Science,
Springer-Verlag, pp 219–233.
MAYRHAUSER, A.V., VANS, A., 1993, “From Program Comprehension to
Tool Requirements for an Industrial Environment,” In: Proceedings of IEEE Workshop
Program Comprehension, pp. 78–86.
MAYRHAUSER, A.V., VANS, A., 1994, “Dynamic Code Cognition Behaviours for
Large Scale Code”. In: Proceedings of Third IEEE Workshop Program
Comprehension, pp 74–81, 1994.
94
MAYRHAUSER, A.V., VANS, 1996, “Identification of Dynamic Comprehension
Processes During Large Scale Maintenance,”. In: IEEE Transactions on. Software
Engineering., v. 22, n. 6, pp. 424–437, June.
MAZÓN, J TRUJILLO., 2008, “An MDA approach for the development of data
warehouses.”. Journal Decision Support Systems – DSS, v. 45, n. 1, pp. 41-58.
MDA, 2010, “Model Driven Architecture”. Disponível em http://www.omg.org/mda/.
Acesso em setembro de 2010.
MDARTE, 2010 , “MDARTE”. Disponível em http://www.softwarepublico.gov.br/ver-
comunidade?community_id=9022831. Acesso em agosto de 2010.
Meyer B., 1988, Object-Oriented Software Construction, Prentice-Hall, New York.
MOF, 2010, “Meta-Object Facility“. Disponível em
http://www.omg.org/cgibin/doc?formal/2002-04-03. Acesso em outubro de 2010.
MVC, 2010, “MODEL VIEW CONTROLLER”. Disponível em
http://java.sun.com/blueprints/patterns/MVCdetailed.html. Acesso em novembro de
2010.
NICKEL, U. A., NIERE, J., 2001, “Modelling and simulation of a material flow
system”. In: Proceedings. of Workshop ’Modellierung’ (Mod), Bad Lippspringe,
Germany.
95
NICKEL, U. A., NIERE, J., WADSACK J. P., UNDORF A. Z, 2000, “Roundtrip
engineering with fujaba”. In: Proceedings of 2nd Workshop on Software-Reengineering
(WSR), Bad Honnef, Germany, August.
NIERE J., SCH¨AFER, W., WADSACK J. P., WENDEHALS L., Welsh J., 2002,
“Towards patternbased design recovery”. In Proceedings of the 24th International
Conference on Software Engineering (ICSE), Orlando, Florida, USA, pp 338–348.
ACM Press.
OCL , 2010, “OBJECT CONSTRAINT LANGUAGE”. Disponível em
http://www.omg.org/spec/OCL/2.0/ . Acesso em novembro de 2010.
OMG, 2010, “OBJECT MANAGEMENT GROUP”. Disponível em
http://www.omg.org . Acesso em agosto de 2010.
OSI, 2011, “OPEN SYSTEMS INTERCONNECTION MODEL”. Disponível em
http://standards.iso.org . Acesso em janeiro de 2011.
PERRY D. E., 1989, “The Inscape Enviroment”. In: Proceedings 11th
International.
Conference on Software Engineering, pp 2-12, IEEE CS Press, May.
PERRY, D.E., WOLF A. L.; 1992, “Foundations for the Study of Software
Architecture”. ACM SIGSOFT Software Engineering Notes, vol. 17, n. 4, pp. 40-52.
96
PHP, 2011, “PHP HYPERTEXT PREPROCESSOR”. Disponível em http://php.net/.
Acesso em fevereiro de 2011.
PINHERO, F.A.C., GOGUEN, J.A., 1996, “An Object-Oriented Tool for Tracing
Requirements,” IEEE Software, v. 13, n. 2, pp. 52–64, Mar.
RAMESH, B., DHAR V., 1992, “Supporting Systems Development Using Knowledge
Captured During Requirements Engineering,”. IEEE Transactions on Software
Engineering, v. 9, n. 2, pp. 498–510, June.
REUS, T; GEERS, H.; DEURSEN, A.; 2006, “Harvesting Software Systems for MDA-
Based Reengineering”. In: European Conference on Model Driven Architecture -
Foundations and Applications – ECMDAFA, pp 213-255,Bilbao, Spain, July.
ROOCK, S., LIPPERT, M., 2006, Refactoring in Large Software Projects: Performing
complex restructurings successfully, John Wiley & Sons.
ROUNTEV, A., VOLGIN, O., REDDOCH, M., 2004, “Control flow analysis for
reverse engineering of sequence diagrams”. Technical Report OSU-CISRC-3/04-TR12,
Ohio State University, Mar.
SILVA, M. A. N., FREITAS, K. , MONTEIRO, R. S. , SOUZA, J. M., 2010 ,
“Automatically generating Component Dependency Diagrams in a Model Driven
Development Scenario”. I Brazilian Workshop on Model - Driven Development -
CBSOFT, Salvador, Bahia, Brazil.
97
SOLOWAY, E., EHRLICH K., 1994, “Empirical Studies of Programming
Knowledge,” IEEE Transactions on Software Engineering., v. 10, n. 5, pp. 595–609.
STRUTS , 2010, “STRUTS”. Disponível em http://struts.apache.org/. Acesso em
dezembro de 2010.
TURVER R.J., MUNRO M., 1994 , “An Early Impact Analysis Technique for Software
Maintenance,” Journal of Software Maintenance — Research and Practice, v. 6, n. 1,
pp. 35–52.
ULRICH, W., 2004, A status on OMG architecture-driven modernization task force. In:
ProceedingsEDOC Workshop on Model-Driven Evolution of Legacy Systems (MELS).
IEEE Computer Society Digital Library.
UML, 2010,“Unified Modeling Language”. Disponível em http://www.uml.org/.
Acesso em agosto de 2010.
VALDERAS, P., FONS, J., PELECHANO, V., 2005, “Transforming web requirements
into navigational models: an MDA based approach”. In: Conceptual Modeling ER 2005
v. 3716, Lecture Notes in Computer Science, Springer, pp 320-336.
VDM , 1990, “VDM and Z: Formal Methods in Software Development”: In: Third
International Symposium of VDM Europe, Kiel, Germany, April 17-21.
98
XML, 2010, “Extensible Markup Language”. Disponível em http://www.w3.org/XML/.
Acesso em outubro de 2010.
WIMMER, M., KRAMLER, G., 2006, “Bridging grammarware and modelware”. In:
Satellite Events at the MoDELS 2005 Conference: MoDELS 2005 International
Workshops, v. 3844, Lecture Notes in Computer Science, Springer-Verlag, pp 159–168.
99
Anexos
100
Anexo 1
Mapeamento dos elementos de modelo aos artefatos gerados.
/**
* Map the elements on a model to one or more artifacts in the final source code
*
* @param outputFile - file representing the generated artifact
* @param template - the Template containing the template path to process
* @param metafacadePackage - the name of the package
* @param templateContext - the context to which variables are added and made
* available to the template engine for processing. This will contain
* any model elements being made avaiable to the template(s) as well
* as properties/template objects.
* @param metafacadeName - the name of the model element
*/
static public void mapModelToImpl( File outputFile, Template template , String
metafacadePackage,final Map templateContext, String metafacadeName){
String space = " ";
String fileName = null;
Boolean isImplementationPoint = null;
Collection<String> modelElementNames = new ArrayList<String>();
Boolean insertContent =false;
String filePath = null;
String modelElementName = null;
try {
101
//Criando um arquivo novo ou abrindo um existente
BufferedWriter out = new BufferedWriter(new FileWriter("Mapeamento.txt", true));
//Criando um arquivo novo para composicao dos elementos de modelo
BufferedWriter compositions = new BufferedWriter(new FileWriter("Compositions.xml",
true));
//reconhecendo que o artefato gerado e uma classe java
if(outputFile.isFile() && ( outputFile.getName().endsWith(".java") ||
outputFile.getName().endsWith(".jsp") ) ){
//armazenando valores de interesse
fileName = outputFile.getName();
isImplementationPoint = !template.isOverwrite();
filePath = outputFile.getCanonicalPath();
//Recuperando o elemento modelElements
ModelElements modelElements = template.getSupportedModeElements();
//Recuperando o nome da variavel que representa o metafacade
String variable = modelElements.getVariable();
Object value = templateContext.get(variable);
//Verficando se e uma colecao de metafacades ou um unico metafacade
if(! template.isOutputToSingleFile()){
102
if (value instanceof MetafacadeBase) {
//composicoes
treatCompositions(compositions, value, template);
Class clazz = value.getClass();
if(clazz.getName().equalsIgnoreCase
("org.andromda.cartridges.bpm4struts.metafacades.CoppetecStrutsJspLogicImpl")
||clazz.getName().equalsIgnoreCase
("org.andromda.cartridges.bpm4struts.metafacades.CoppetecStrutsActionLogicImpl")
)
{
modelElementName =treatActionsStatesOrTransitions(value);
}else{
Method method = clazz.getMethod("getFullyQualifiedName", null);
modelElementName = (String) method.invoke(value, null);
}
insertContent=true;
modelElementNames.add(modelElementName);
}//fim do ifMetafacade
fim do if outputSingleFile
}else{
if (value instanceof Collection) {
Collection testes = (Collection) value;
for (Object v : testes) {
103
if(v instanceof MetafacadeBase){
//composicoes
treatCompositions(compositions, v , template);
Class clazz2 = v.getClass();
Method method2 =
clazz2.getMethod("getFullyQualifiedName", null);
modelElementName =
(String) method2.invoke(v, null);
insertContent=true;
modelElementNames.add(modelElementName);
}
}
}
}// fim do else
String group = null;
String namespaceCartridge = template.getCartridge().getNamespace();
group =Architecture.getCartridge().
get(namespaceCartridge).
get(template.getOutlet());
if(group==null){
group = Architecture.getNamespaces().get(template.getCartridge().getNamespace());
}
//Preenchendo o conteudo do arquivo com os valores de interesse
if(insertContent){
out.write(fileName);
104
out.write(space);
out.write(filePath);
out.write(space);
out.write(isImplementationPoint.toString());
out.write(space);
out.write(group);
out.write(space);
for (String name : modelElementNames) {
out.write(name);
out.write(space);
}
out.write("\n");
}// fim do if insert
else{//fim do if do models
out.write(fileName);
out.write(space);
out.write(filePath);
out.write(space);
out.write(isImplementationPoint.toString());
out.write(space);
out.write(group);
out.write(space);
out.write("---");
out.write("\n");
}//fim do else
}//fim do if output
//Fechando o arquivo
out.close();
105
modelElementNames.clear();
} catch (Exception e) {
e.printStackTrace();
}
return;
}
/**
* Determine if this file was already generated
*
* @param canonicalPathFile - path of the file
* @return true if this file wasn't generated (false otherwise)
*/
static public boolean validateNameFile(String canonicalPathFile){
boolean validation = false;
if(nameFiles.get(canonicalPathFile) == null){
nameFiles.put(canonicalPathFile, canonicalPathFile);
validation = true;
}
return validation;
106
}
/**
* Return the unique name of model element
* @param value - Metaface Class
* @return a name
* @throws Exception
*/
static private String treatActionsStatesOrTransitions(Object value) throws Exception{
Class clazz = value.getClass();
//recupero o objeto useCase
Method getUseCase = clazz.getMethod("getUseCase", null);
Object useCase = getUseCase.invoke(value, null);
//recuperando o nome do pacote
Method getPackageName = clazz.getMethod("getPackageName", null);
String packageName = (String)getPackageName.invoke(value, null);
//recuperando o nome do elemento
Method getNameElement = clazz.getMethod("getName", null);
String name = (String)getNameElement.invoke(value, null);
//invoco o meto getName do caso de uso
Class useCaseClass = useCase.getClass();
Method getNameUseCase = useCaseClass.getMethod("getName", null);
String useCaseName = (String)getNameUseCase.invoke(useCase, null);
//recuperando uma instancia do diagrama de atividades
107
Method getActivityDiagram = useCaseClass.getMethod("getFirstActivityGraph", null);
Object activityDiagram = getActivityDiagram.invoke(useCase, null);
//recupernado o nome do digrama de atividades
Class ActivityDiagramClass = activityDiagram.getClass();
Method getName = ActivityDiagramClass.getMethod("getName", null);
String activityDiagramName = (String)getName.invoke(activityDiagram, null);
return packageName + "." + useCaseName + "." +activityDiagramName +"."+ name;
}
/**
* Build a XML File representing the navegability between model elements
*
* @param composition - XML file
* @param value - Metafacade class
* @param template - the Template containing the template path to process
*/
static private void treatCompositions(BufferedWriter composition , Object value , Template template){
try {
Class clazz = value.getClass();
String className = clazz.getName();
if(className.equalsIgnoreCase("
org.andromda.cartridges.bpm4struts.metafacades.CoppetecStrutsJspLogicImpl")
|| className.equalsIgnoreCase("
108
org.andromda.cartridges.bpm4struts.metafacades.CoppetecStrutsActionLogicImpl")){
//recuperando o nome do elemento
Method getNameElement = clazz.getMethod("getName", null);
String nameElement = (String)getNameElement.invoke(value, null);
//recuperando o nome do pacote
Method getPackageName = clazz.getMethod("getPackageName", null);
String packageName = (String)getPackageName.invoke(value, null);
//recupero o objeto useCase
Method getUseCase = clazz.getMethod("getUseCase", null);
Object useCase = getUseCase.invoke(value, null);
//invoco o meto getName do caso de uso
Class useCaseClass = useCase.getClass();
Method getNameUseCase = useCaseClass.getMethod("getName", null);
String useCaseName = (String)getNameUseCase.invoke(useCase, null);
//recuperando o nome do pacote
Method getPackageNameUse = useCaseClass.getMethod("
getPackageName", null);
String packageNameUseCase = (String)getPackageNameUse.invoke(
useCase, null);
//recuperando uma instancia do diagrama de atividades
Method getActivityDiagram = useCaseClass.getMethod("
getFirstActivityGraph", null);
Object activityDiagram = getActivityDiagram.invoke(useCase, null);
109
//recupernado o nome do digrama de atividades
Class ActivityDiagramClass = activityDiagram.getClass();
Method getName = ActivityDiagramClass.getMethod("getName", null);
String activityDiagramName = (String)getName.invoke(
activityDiagram, null);
//definindo o grupo ao qual pertence
composition.write("\n");
composition.write("<composition>");
composition.write("\n");
composition.write("<useCase>");
composition.write(packageNameUseCase + "."+ useCaseName);
composition.write("</useCase>");
composition.write("\n");
composition.write("<activityDiagram>");
composition.write(activityDiagramName);
composition.write("</activityDiagram>");
composition.write("\n");
composition.write("<modelElement>");
composition.write(packageName + "." + useCaseName + "."
+activityDiagramName +"."+ nameElement);
composition.write("</modelElement>");
composition.write("\n");
composition.write("<nameElement>");
composition.write(nameElement);
composition.write("</nameElement>");
composition.write("\n");
composition.write("<if>");
110
composition.write("1");
composition.write("</if>");
composition.write("\n");
composition.write("</composition>");
composition.write("\n");
composition.close();
}elseif(className.equalsIgnoreCase("
org.andromda.cartridges.bpm4struts.metafacades.StrutsControllerLogicImpl")){
//recuperando o nome da classe
Method getFullyQualifiedName = clazz.getMethod(
"getFullyQualifiedName", null);
String modelElement = (String) getFullyQualifiedName.invoke(
value, null);
Method getmodelElementName = clazz.getMethod("getName", null);
String modelElementName = (String)
getFullyQualifiedName.invoke(value, null);
//recupero o objeto useCase
Method getUseCase = clazz.getMethod("getUseCase", null);
Object useCase = getUseCase.invoke(value, null);
//invoco o meto getName do caso de uso
Class useCaseClass = useCase.getClass();
Method getNameUseCase = useCaseClass.getMethod("
111
getName", null);
String useCaseName = (String)getNameUseCase.invoke(
useCase, null);
//recuperando o nome do pacote
Method getPackageName = useCaseClass.getMethod("
getPackageName", null);
String packageName = (String)getPackageName.invoke(
useCase, null);
//recuperando uma instancia do diagrama de atividades
Method getActivityDiagram = useCaseClass.getMethod("
getFirstActivityGraph", null);
Object activityDiagram = getActivityDiagram.invoke(useCase, null);
//recupernado o nome do digrama de atividades
Class ActivityDiagramClass = activityDiagram.getClass();
Method getName = ActivityDiagramClass.getMethod("getName", null);
String activityDiagramName = (String)getName.invoke(
activityDiagram, null);
//definindo o grupo ao qual pertence
String group = null;
String namespaceCartridge = template.getCartridge().getNamespace();
group =Architecture.getCartridge().get(namespaceCartridge).
get(template.getOutlet());
if(group==null){
112
group = Architecture.getNamespaces().
get(template.getCartridge().getNamespace());
}
composition.write("\n");
composition.write("<composition>");
composition.write("\n");
composition.write("<useCase>");
composition.write(packageName + "." + useCaseName);
composition.write("</useCase>");
composition.write("\n");
composition.write("<activityDiagram>");
composition.write(activityDiagramName);
composition.write("</activityDiagram>");
composition.write("\n");
composition.write("<modelElement>");
composition.write(modelElement);
composition.write("</modelElement>");
composition.write("\n");
composition.write("<nameElement>");
composition.write(modelElementName);
composition.write("</nameElement>");
composition.write("\n");
composition.write("<group>");
composition.write(group);
composition.write("</group>");
composition.write("\n");
composition.write("<if>");
composition.write("2");
composition.write("</if>");
113
composition.write("\n");
composition.write("</composition>");
composition.write("\n");
composition.close();
}else if(className.equalsIgnoreCase(
"org.andromda.cartridges.bpm4struts.metafacades.
CoppetecStrutsControllerOperationLogicImpl")){
//recuperando o nome do metodo do Controle
Method getNameMethod = clazz.getMethod("getName", null);
String modelElement = (String) getNameMethod.invoke(value, null);
//recuperando a classe de controle
Method getOwner = clazz.getMethod("getOwner", null);
Object controller = getOwner.invoke(value, null);
Class controllerClass = controller.getClass();
Method getFullyQualifiedName = controllerClass.getMethod(
"getFullyQualifiedName", null);
String fullyNameClass = (String) getFullyQualifiedName.invoke(
controller, null);
//recupero o objeto useCase
Method getUseCase = controllerClass.getMethod("
getUseCase", null);
Object useCase = getUseCase.invoke(controller, null);
114
//invoco o meto getName do caso de uso
Class useCaseClass = useCase.getClass();
Method getNameUseCase = useCaseClass.getMethod("
getName", null);
String useCaseName = (String)getNameUseCase.invoke(
useCase, null);
//recuperando o nome do pacote
Method getPackageName = useCaseClass.getMethod("
getPackageName", null);
String packageName = (String)getPackageName.invoke(
useCase, null);
//recuperando uma instancia do diagrama de atividades
Method getActivityDiagram = useCaseClass.getMethod(
"getFirstActivityGraph", null);
Object activityDiagram = getActivityDiagram.invoke(useCase, null);
//recupernado o nome do digrama de atividades
Class ActivityDiagramClass = activityDiagram.getClass();
Method getName = ActivityDiagramClass.getMethod(
"getName", null);
String activityDiagramName = (String)getName.invoke(
activityDiagram, null);
composition.write("\n");
composition.write("<composition>");
composition.write("\n");
composition.write("<useCase>");
115
composition.write(packageName + "." + useCaseName);
composition.write("</useCase>");
composition.write("\n");
composition.write("<activityDiagram>");
composition.write(activityDiagramName);
composition.write("</activityDiagram>");
composition.write("\n");
composition.write("<modelElement>");
composition.write(fullyNameClass + "." + modelElement);
composition.write("</modelElement>");
composition.write("\n");
composition.write("<nameElement>");
composition.write(modelElement);
composition.write("</nameElement>");
composition.write("\n");
composition.write("<if>");
composition.write("3");
composition.write("</if>");
composition.write("\n");
composition.write("</composition>");
composition.write("\n");
composition.close();
}
} catch (Exception e) {
// TODO Auto-generated catch block
System.out.println("ERRO !!!! " + value.getClass().getName());
116
e.printStackTrace();
}
}
Anexo 2
Classificação dos grupos dos artefatos em relação ao seu caminho lógico (outlet).
public class Architecture {
private static HashMap<String, Map<String, String>> cartridge = new HashMap<String,
Map<String,String>>();
private static Map<String,String> namespaces = new HashMap<String, String>();
static{
//Definindo a regra geral para o grupo
namespaces.put("ejb", "service");
namespaces.put("java", "common");
namespaces.put("hibernate", "domain");
namespaces.put("bpm4struts", "view");
//Definindo regra especifica para o grupo dos artefatos
//gerados pelo STRUTS a partir do caminho lógico
Map<String,String> outletsStruts = new HashMap<String, String>();
String groupStruts = "view";
outletsStruts.put("layout-util", groupStruts);
outletsStruts.put("layout-util-impl", groupStruts);
outletsStruts.put("shared-actions", groupStruts);
outletsStruts.put("shared-pages-impl", groupStruts);
outletsStruts.put("actions", groupStruts);
117
outletsStruts.put("pages", groupStruts);
outletsStruts.put("forms", groupStruts);
outletsStruts.put("controller-impls", groupStruts);
outletsStruts.put("controllers", groupStruts);
outletsStruts.put("shared-pages", groupStruts);
outletsStruts.put("shared-actions-impl", groupStruts);
outletsStruts.put("session-objects", groupStruts);
outletsStruts.put("controleAcesso", groupStruts);
outletsStruts.put("controleAcessoImpl", groupStruts);
cartridge.put("bpm4struts", outletsStruts);
//Definindo regra especifica para o grupo dos artefatos
//gerados pelo HIBERNATE a partir do caminho lógico
Map<String,String> outletsHibernate = new HashMap<String, String>();
String groupHibernate="domain";
outletsHibernate.put("entities", groupHibernate);
outletsHibernate.put("entity-impls", groupHibernate);
outletsHibernate.put("transfers", groupHibernate);
outletsHibernate.put("transfer-impls", groupHibernate);
cartridge.put("hibernate", outletsHibernate);
//Definindo regra especifica para o grupo dos artefatos
//gerados pelo EJB a partir do caminho lógico
Map<String,String> outletsEJB = new HashMap<String, String>();
String groupEJB="service";
outletsEJB.put("session-beans", groupEJB);
outletsEJB.put("shared-session-beans", groupEJB);
outletsEJB.put("session-impls", groupEJB);
outletsEJB.put("pbi", groupEJB);
118
outletsEJB.put("controleAcessoImpl", groupEJB);
outletsEJB.put("controleAcesso", groupEJB);
outletsEJB.put("shared-session-beans-impl", groupEJB);
cartridge.put("ejb", outletsEJB);
//Definindo regra especifica para o grupo dos artefatos
//gerados pelo JAVA a partir do caminho lógico
Map<String,String> outletsJava = new HashMap<String, String>();
String groupJava = "common";
outletsJava.put("value-objects", groupJava);
outletsJava.put("logger", groupJava);
outletsJava.put("exceptions", groupJava);
outletsJava.put("enumerations", groupJava);
outletsJava.put("util", groupJava);
cartridge.put("java", outletsJava);
}
public static Map<String, Map<String,String>> getCartridge() {
return cartridge;
}
public static Map<String, String> getNamespaces() {
return namespaces;
}
}