You are on page 1of 90

UNIVERSIDADE FEDERAL DO PARÁ

CENTRO DE CIÊNCIAS EXATAS E NATURAIS


CURSO BACHARELADO EM CIÊNCIA DA COMPUTAÇÃO

Helder Klemp Correa da Silva


Viviane Soares Grieco

TÉCNICAS DE PLANEJAMENTO EM ARQUITETURAS:


SOLUÇÕES UTILIZANDO J2EE, AOP E IOC

Belém - PA
2006
UNIVERSIDADE FEDERAL DO PARÁ
CENTRO DE CIÊNCIAS EXATAS E NATURAIS
CURSO BACHARELADO EM CIÊNCIA DA COMPUTAÇÃO

Helder Klemp Correa da Silva


Viviane Soares Grieco

TÉCNICAS DE PLANEJAMENTO EM ARQUITETURAS:


SOLUÇÕES UTILIZANDO J2EE, AOP E IOC

Trabalho de Conclusão de Curso


apresentado para obtenção do grau em
Bacharel em Ciência da Computação.

Orientador: Prof. Esp. Fernando Nazareno


Nascimento Farias

Belém - PA
2006
UNIVERSIDADE FEDERAL DO PARÁ
CENTRO DE CIÊNCIAS EXATAS E NATURAIS
CURSO BACHARELADO EM CIÊNCIA DA COMPUTAÇÃO

Helder Klemp Correa da Silva


Viviane Soares Grieco

NOVAS TÉCNICAS DE PLANEJAMENTO EM


ARQUITETURAS DISTRIBUÍDAS: SOLUÇÕES UTILIZANDO
J2EE, AOP E IOC

Trabalho de Conclusão de Curso apresentado para obtenção do


grau em Bacharel em Ciência da Computação

Data da defesa: 25 de abril de 2006.


Conceito:

Banca Examinadora:

Prof. Esp. Fernando Nazareno Nascimento Farias


Departamento de Informática / UFPA – Orientador

Prof. MsC. Alfredo Braga Furtado


Departamento de Informática / UFPA – Membro

Prof. MsC. Arnaldo Prado Jr.


Departamento de Informática / UFPA – Membro
AGRADECIMENTOS

[Helder]: Agradeço a Deus, que sempre me acompanhou nas difíceis jornadas da


vida, Aos meus pais, que me deram a melhor herança que se pode ter, me
deram educação e respeito. Às minhas irmãs, que sempre estiveram ao
meu lado. Aos amigos que aprendi e aprendo sempre. Aos professores da
UFPA, que me mostraram o caminho a seguir; E um agradecimento
especial à minha noiva, que está presente em todas as ocasiões boas e
ruins, e que me ajudou no presente trabalho.

[Viviane]: Agradeço a Deus, que sempre esteve e estará ao meu lado, me dando
forças e saúde para seguir em frente. À minha mãe, pelo amor, carinho e
paciência sempre presente. Aos meus irmãos pelo exemplo a ser seguido e
pela força. Ao meu noivo pela ajuda, amor, carinho e companheirismo. Ao
meu orientador, pela ajuda e paciência no desenvolvimento deste trabalho.
Aos professores, pelos ensinamentos ao longo dos cinco anos de curso,
auxiliando no meu crescimento profissional. Aos meus amigos, pelo
companheirismo e incentivo.
"Cada momento da nossa vida é um
ensaio para o que vem a seguir".

Marcio Giulierme
6

SUMÁRIO

Introdução................................................................................................................15
Capítulo 1. Princípios Arquiteturais ................................................................................ 17
1.1. Arquitetura X Designer ................................................................................18
1.2. Arquitetura no Processo de Desenvolvimento de Sistemas........................20
1.3. Princípios de arquiteturas............................................................................21
1.3.1. Capacidades de uma arquitetura..........................................................24
Capítulo 2. Padrões de Projetos e melhores práticas para projetos J2EE ................ 26
2.1. Padrões GoF...............................................................................................27
2.2. Padrões J2EE .............................................................................................28
2.2.1. Patterns da camada de apresentação ..................................................28
2.2.2. Patterns de camada de negócios .........................................................30
2.2.3. Patterns de camada de Integração.......................................................31
2.3. Benefícios e Problemas do uso de Padrões de Projeto ..............................32
Capítulo 3. Arquiteturas J2EE......................................................................................... 34
3.1. Container WEB em J2EE ............................................................................34
3.2. EJB e o modelo de container ......................................................................35
3.3. Performance com EJB ................................................................................37
3.4. Tipos de EJB...............................................................................................39
3.4.1. Session Beans......................................................................................39
3.4.2. Entity Bean ...........................................................................................41
3.4.3. Message Driven Bean ..........................................................................42
Capítulo 4. Aplicando a tecnologia J2EE e seus problemas ....................................... 45
4.1. Quando aplicações distribuídas são apropriadas?......................................45
4.1.1. Aplicações Distribuídas e Escalabilidade .............................................45
4.1.2. Aplicações Distribuídas e Confiabilidade ..............................................47
4.1.3. Quando se deve usar EJB? ..................................................................48
4.1.3.1. Gerenciamento de transações .......................................................48
4.1.3.2. Aspectos negativos de EJB............................................................49
4.2. Acesso a dados em aplicações J2EE .........................................................50
Capítulo 5. Uso de arquiteturas leves com a adoção de novos conceitos................. 52
5.1. Diminuição de Acoplamento ........................................................................52
5.2. Inversão de Controle ...................................................................................54
5.2.1. Formas de implementação de Inversão de Controle ............................54
5.2.1.1. Setter Injection ...............................................................................54
5.2.1.2. Constructor Injection ......................................................................56
5.2.1.3. Interface Injection...........................................................................57
5.2.2. Constructor Injection x Setter Injection .................................................60
5.3. Testabilidade em Aplicações J2EE..............................................................61
5.3.1. Técnica Funcional.................................................................................62
5.3.2. Técnica Estrutural.................................................................................64
5.3.3. Testes na metodologia Extreme Programing (XP)................................64
5.3.3.1. Testes Unitários no XP...................................................................65
5.3.3.2 Testes de Aceitação........................................................................66
5.3.4. Testes com EJB....................................................................................66
5.4. Camada de acesso a dados simplificada ....................................................68
5.4.1. Utilizando Hibernate com Spring ..........................................................69
5.4.2. Utilizando IBatis com Spring .................................................................71
5.5. Programação Orientada a Aspectos............................................................72
6
7

5.5.1. Programação Orientada a Aspectos com Spring ..................................73


5.6. Gerenciamento robusto e elegante de Transações.....................................74
5.7. Uso de mensagens Assíncronas.................................................................76
5.7.1. Message Driven Pojos (MDP)..............................................................78
Capítulo 6. Realização de testes de carga em aplicações usando arquiteturas EJB e
arquiteturas leves em J2EE.......................................................................................80
6.1. Testes de Carga ..........................................................................................80
6.2. Importância dos Testes de Carga ...............................................................81
Conclusão ................................................................................................................86
Referências Bibliográficas .....................................................................................87

7
8

LISTA DE FIGURAS

Figura 1.1: Processo de desenvolvimento em cascata.......................................... 19


Figura 1.2: pontos onde as atividades de arquitetura são mais visíveis................ 20
Figura 1.3: Camadas arquitetônicas de um sistema.............................................. 21
Figura 3.1: Interação entre cliente e servidor Web................................................. 34
Figura 3.2: Modelo de Pools de EJB de sessão..................................................... 40
Figura 3.3: Funcionamento de um Message Driven Bean..................................... 43
Figura 4.1: Arquitetura J2EE robusta e escalável.................................................. 47
Figura 5.1: Funcionamento dos testes funcionais.................................................. 62
Figura 5.2: Funcionamento de testes unitários...................................................... 63
Figura 5.3: Arquitetura básica de uma JMS........................................................... 76
Figura 5.4: Funcionamento do MDP....................................................................... 78

Figura 6.1: Interface do JMeter.............................................................................. 80

Figura 6.2: Fluxo da aplicação desenvolvida......................................................... 81


Figura 6.3: Gráfico de resultados obtidos na aplicação que utiliza Spring............. 82
Figura 6.4: Gráfico de resultados obtidos na aplicação que utiliza EJB................ 83

8
9

LISTA DE TABELAS

Tabela 5.1: Comparativo entre ferramentas de teste funcional.............................. 62


Tabela 5.2: Tabela comparativa das três alternativas de testes com EJB............. 67
Tabela 6.1: Tabela comparativa na aplicação que utiliza Spring........................... 81
Tabela 6.2: Tabela comparativa na aplicação que utiliza EJB............................... 82

9
10

LISTAGENS

Listagem 5.1: Código de acesso a componentes EJB........................................... 52


Listagem 5.2: Exemplo de Interface com método findAll....................................... 54
Listagem 5.3.: Exemplo de classe que implementa o método setter..................... 54
Listagem 5.4.: Exemplo de classe que possui um atributo posteriormente
injetado................................................................................................................... 54
Listagem 5.5.: Arquivo XML com configuração da injeção..................................... 55
Listagem 5.6.: Classe onde será feita a injeção através do construtor.................. 55
Listagem 5.7.: Classe onde ao invés de receber um atributo, passa a ter a
lógica no seu construtor......................................................................................... 55
Listagem 5.8.: Arquivo Java de configuração de injeção....................................... 56
Listagem 5.9.: Interface que irá injetar objetos através do Interface Injection....... 56
Listagem 5.10.: Classe que implementa a interface do interface
injection.................................................................................................................. 57
Listagem 5.11.: Interface que deverá ser implementada....................................... 57
Listagem 5.12.: Classe que implementa a interface da listagem 5.11................... 57
Listagem 5.13.: Arquivo de configuração para o Interface Injection....................... 58
Listagem 5.14: Configuração de DataSource para uso de Spring com hibernate. 69
Listagem 5.15: Trecho do arquivo de configuração do Spring com IBatis (struts-
config.xml).............................................................................................................. 70
Listagem 5.16: Trecho do arquivo de declaração dos mapeamentos
(sqlMapConfig.xml)................................................................................................. 70
Listagem 5.17: Exemplo de arquivo de mapeamento de um determinado Bean
(Client.xml)............................................................................................................. 71
Listagem 5.18: Arquivo de configuração de demarcação transacional para os
métodos do serviço................................................................................................ 75

10
11

LISTA DE SIGLAS
AOP Programação Orientada a Aspectos
API Application Programming Interface
ASP Active Server Pages
BMP Bean Manager Persistence
CMP Container Manager Persistence
DAO Data Acess Object
DBMS Database Manager System
EIS Enterprise Information System
EJB Enterprise Java Beans
GOF Gang Of Four
HQL Hibernate Query Language
HTML Hyper Text Markup Language
http HyperText Transfer Protocol
IDE Integrated Development Environments
IIOP Internet Inter-Orb Protocol
IOC Inversion of Control
J2EE Java2 Platform Enterprise Edition
JDBC Java Database Connectivity
JMS Java Message Service
JNDI Java Naming and Directory Interface
JSP Java Server Pages
JTA Java Transaction API
LDAP Lightweight Directory Acess Protocol
LRU Last Recent Used
MDB Message Driven Bean
MDP Message Driven Pojo
MVC Model-View-Controller
POJO Plain Old Java Objects
RMI Remote Method Invocation
RPC Remote Procedure Call
SQL Structured Query Language
XML Extensible Markup Language
XP Extreme Programing

11
12

RESUMO

No cenário atual do desenvolvimento de sistemas distribuídos, percebe-se uma


árdua e burocrática necessidade de codificação e manutenção de código. A atual
arquitetura J2EE disponível fornece ao arquiteto desenvolvedor uma grande
variedade de alternativas, porém, agregado a isso, uma série de regras surgem
tornando o desenvolvimento mais burocrático.
Com isso, vem se tornando cada vez mais presente o uso de técnicas que
visam um menor esforço para a arquitetura e o desenvolvimento de sistemas
distribuídos. Dentre essas técnicas, podem-se citar o uso de padrões de projeto para
evitar a repetição desnecessária de código e aumentar a modularização aliado à
conceitos novos de arquiteturas de software, tal como Containeres Leves, em
oposição aos atuais servidores de aplicação, e a prática de Inversão de Controle,
que promove uma grande diminuição de acoplamento entre as camadas de um
sistema.
O presente trabalho visa esclarecer os pontos negativos da arquitetura J2EE e
propor uma arquitetura mais simples, usando de novos conceitos, como Inversão de
Controle, container leve e Orientação a Aspectos.

Palavras-chaves: Arquitetura de Software, EJB, Escalabilidade, Inversão de


Controle, J2EE, Padrões de Projeto, Sistemas Distribuídos, Spring.

12
13

ABSTRACT

In the current scene in the development of distributed systems, one perceives


an arduous and bureaucratic necessity of codification and maintenance of code.
Current available architecture JÈE supplies to the developer architect a great variety
of alternatives, however, added to this, a series of rules appears becoming the
development most bureaucratic.
With this, it comes if becoming each more present time the use of techniques
that aim at a lesser effort for the architecture and the development of distributed
systems. Amongst these techniques, can be cited the use of design patterns to
prevent the unnecessary code duplication and to increase the modularity ally to the
new concepts of software architectures, such as Light Containers, in opposition to
the current application servers, and the use of Inversion of Control, that promotes a
great reduction of coupling between the system layers.
This work aims at to clarify the negative points of J2EE architecture and to
consider a simpler architecture, using of new concepts, as light container, Inversion
of Control and Aspects Orientation.

Keywords: Software Architecture, EJB, Scalability, Inversion of Control, J2EE,


Design Patterns, Distributed Systems, Spring.

13
14

Introdução

Ao longo dos anos, o processo de desenvolvimento de software está em


constante evolução e mudança. Antes da explosão da Internet, por volta dos anos
90, a maioria das aplicações em produção eram centralizadas, na maior parte das
vezes rodavam em um mainframe1 com várias estações magras2 que apenas
acessavam os dados. Levando-se em consideração que essas interfaces eram
extremamente pobres, iniciou-se a demanda para aplicações mais ricas e mais
agradáveis para o cliente. Além da popularização de bancos de dados relacionais e
o surgimento de aplicações gráficas no cliente, tornou-se padrão o desenvolvimento
de arquiteturas em duas camadas (uma sendo o servidor de banco de dados e a
outra seria a camada de apresentação).
Com a explosão da Internet, diversas empresas viram nesta grande rede uma
forma de expor seus serviços para uma quantidade infinita de consumidores. Porém,
diversos fatores devem ser levantados acerca de arquiteturas expostas para a
Internet, uma vez que uma quantidade enorme de acessos ao sistema não pode
diminuir sua qualidade de resposta.
Mesmo com o “boom” causado pela Internet, fica claro que não se podia
abandonar os “Livros de Registros”, as aplicações de legado3 com informações
cruciais e que ainda estavam em produção, tampouco seria viável uma reescrita
dessas aplicações, visto que as mesmas funcionam perfeitamente. Uma solução
para a disponibilização dos “livros de registros” e para o desenvolvimento de novas
funcionalidades para a Internet é uma arquitetura que se conecte facilmente a dados
existentes.
Nesse contexto, a arquitetura J2EE vem se mostrando um padrão aberto de
especificações que contemplam uma fácil conectividade com outras aplicações
como interação maleável com o usuário e um modelo flexível de componentes de
negócio, tornando-se uma ótima opção para arquitetos de software elaborar
aplicações robustas e de qualidade, para uma vasta gama de usuários acessando
através de diversas formas interativas como Internet, telefone celular, entre outros.

1
Computador de grande porte, dedicado normalmente ao processamento de um volume grande de informações.
2
Termo utilizado para evidenciar arquiteturas que não possuem grande processamento em seus clientes.
3
Termo com significado de herança, normalmente sistemas e hardware antigos de empresas que guardam uma
grande quantidade de informação.

14
15

Porém, com a adoção de plataformas J2EE, percebeu-se que, além de


solucionar de forma elegante diversos problemas da indústria de software, surgiram
ao longo do tempo alguns problemas relacionados, principalmente, à burocracia de
seu desenvolvimento e manutenção e a pouca testabilidade de aplicações.
O presente trabalho aborda os princípios arquiteturais do J2EE, dando ênfase
em seu modelo de container usando os componentes EJB, além de apontar suas
desvantagens e propor melhores práticas de desenvolvimento para amenizar a
curva de esforço em sua fase de construção.
Uma série de novos conceitos é apresentada, a fim de elaborar uma arquitetura
que apresente todos os fundamentos do J2EE por completo, porém de uma forma
bem mais elegante e fácil de desenvolver e testar.
Dentre tais objetivos este documento está organizado em seis (6) seções, além
desta introdução e da conclusão. Para se ter uma idéia, o capítulo 1 apresenta os
princípios arquiteturais, onde são apresentados conceitos básicos da arquitetura de
um sistema. Logo em seguida, o capítulo 2 enfatiza os principais padrões de projeto
utilizados. O capítulo 3, por sua vez, apresenta uma proposta de arquitetura de
sistemas voltados para a Web. Já o capítulo 4, mostra as dificuldades encontradas
no desenvolvimento de um sistema J2EE, cujas soluções para estas dificuldades
são propostas no capítulo 5, que propõe uma arquitetura mais leve, solucionando
alguns problemas encontrados no capítulo 4. Finalmente, no capítulo 6, são
mostrados resultados comparativos de testes feitos em uma aplicação desenvolvida
utilizando tanto os conceitos J2EE completos como arquiteturas leves.

15
16

Capítulo 1

Princípios Arquiteturais

Muito se fala de arquiteturas de software, porém, para que não haja dúvidas
seguintes no trabalho, faz-se necessária uma explanação sobre sua origem e seu
significado tanto na área tecnológica como em outras áreas originais.
A palavra “Arquiteto” tem origem grega, vem de Architekton, que significa
“construtor chefe”. O termo era usado na Grécia antiga para designar o mestre de
obras dos antigos templos gregos. Pode-se dizer que o objetivo do arquiteto é
sempre reorganizar o ambiente, construir, arquitetar e modelar, sejam edifícios,
instituições ou teorias. Mas qual seria o propósito do arquiteto nos dias de hoje?
Segundo [ALL2003], os arquitetos visualizam o comportamento de um sistema. Eles
criam a planta para os requisitos de sistema funcionais e não-funcionais.
Nesse contexto, a formalização da arquitetura como uma disciplina de
engenharia para o desenvolvimento de software começou com Mary Shaw e David
Garlan, com a publicação do livro “Software Architecture. Perspectives on an
Emerging Discipline”, em 1996 [SHA1996]. A necessidade de várias visões e vários
níveis de abstração na modelagem dos requisitos que serão implementados era
percebida pelos projetistas, mas não registradas até então. Com esta formalização,
surge também o papel do arquiteto de software.
A definição clássica de arquitetura apresentada por [SHA1996] diz que
arquitetura de software define o que é o sistema em termos de componentes
computacionais e os relacionamentos entre estes componentes.
Semelhante a esta definição, [BAS1998] diz que arquitetura de software são as
estruturas que incluem componentes, suas propriedades externas e os
relacionamentos entre eles, constituindo uma abstração do sistema. Esta abstração
suprime detalhes de componentes que não afetam a forma como eles são usados
ou como eles usam outros componentes, auxiliando o gerenciamento da
complexidade.
Para [JAZ2000], a arquitetura de software é colocada como uma ferramenta
para lidar com a complexidade do software e enfatizam que arquitetura deve
satisfazer os requisitos funcionais e não-funcionais do sistema, incrementando a
definição de que arquitetura de software é o conjunto de componentes e seus

16
17

relacionamentos. Portanto, é possível notar que a arquitetura de software é mais do


que a descrição dos componentes que a compõem e do relacionamento entre eles.
A arquitetura é a interface entre duas partes distintas: o problema de negócio e a
solução técnica [AST1998].

1.1. Arquitetura X Designer

Muitas pessoas levam a comparar o arquiteto com o designer (projetista) de


software. Deve-se levar em consideração que o papel do arquiteto é a elaboração da
estrutura do sistema, seus design patterns4 arquiteturais, a escolha ou elaboração
de frameworks5 para o desenvolvimento de componentes, o arquiteto se preocupa
com a não funcionalidade do sistema em um nível de abstração muito elevado,
enquanto que o projetista se preocupa com os casos de uso de negócios,
convertendo os objetos de domínio em um modelo de objeto técnico, operando em
um nível de abstração bem mais baixo visando à implementação de funcionalidades.
O processo de arquitetura de software compreende muitas atividades, dentre
as quais é possível destacar [BAS1998]:
• Elaboração do modelo de negócio para o sistema: o arquiteto deve
contribuir com o seu conhecimento para analisar o custo do sistema, o
tempo de desenvolvimento, as restrições de mercado (público-alvo) e
interfaces com outros sistemas para que a arquitetura possa alcançar
os objetivos do negócio.
• Entendimento dos requisitos: o arquiteto deve contribuir com as
técnicas de levantamento de requisitos a fim de obter o modelo do
domínio.
• Criação ou seleção de uma arquitetura, considerando:
o Identificação dos componentes e suas interações;
o Identificação das dependências de construção;
o Escolha de tecnologias que suportem a implementação.
• Representação da arquitetura e divulgação: os participantes
envolvidos precisam entender a arquitetura. Os desenvolvedores e
testadores necessitam compreender o trabalho que lhes foi atribuído e

4
Conceito visto no capítulo 2.
5
Estrutura de suporte definida em que um outro projeto de software pode ser organizado e desenvolvido.

17
18

o gestor deve entender as implicações do planejamento estratégico


sugerido pelo arquiteto.
• Implementação do sistema baseado na arquitetura: os
desenvolvedores devem se restringir às estruturas e protocolos
definidos na arquitetura.
• Análise ou avaliação da arquitetura: os arquitetos devem
supervisionar e verificar a adequação da arquitetura, registrando
impactos, riscos e dificuldades. Estas informações contribuem para a
evolução da arquitetura em versões posteriores do sistema.
Para cada uma das atividades que o processo da arquitetura de software
propõe, há papéis bem definidos que são associados às pessoas que participam
deste processo. Os participantes (também referenciados como stakeholders)
identificados são [BEN1996]:
• Analista de requisitos: identificador de requisitos funcionais e não-
funcionais do sistema;
• Arquiteto: criador da arquitetura do sistema (partição, prevenção de
mudanças e evoluções, divulgação), identificador de outros requisitos
não-funcionais que a arquitetura deve atender e acompanhante do
processo de desenvolvimento da arquitetura proposta;
• Projetista (designer) e/ou Desenvolvedor: implementador dos
componentes propostos no particionamento de acordo com a plataforma
tecnológica escolhida.
Apesar de existirem vários participantes com papéis distintos na definição da
arquitetura, os modelos arquiteturais devem ser finalizados pelo arquiteto líder ou
por uma equipe pequena de arquitetos, com um deles exercendo a liderança. A
importância disto se deve ao fato da arquitetura servir de apoio e guia para a
construção do sistema.
Isto contribui para a evolução da disciplina, arquitetura de software, mostrando
que arquitetura é mais do que o resultado de requisitos técnicos que implicam no
desenho de infra-estrutura do sistema. Ela compreende outros tipos de estrutura.
Isto significa que há mais que um tipo de componente, como módulos
implementáveis e processo; mais que um tipo de interação entre os componentes,
como subdivisão e sincronia; e mais que um contexto, como ambiente de
desenvolvimento e ambiente de operação [BAS1998].

18
19

1.2. Arquitetura no Processo de Desenvolvimento de Sistemas

O processo clássico de desenvolvimento de sistemas chamado waterfall (ou


cascata) [PRE2001] que é encontrado na disciplina de Engenharia de Software
possui uma seqüência de fases bem definidas que guiam o desenvolvimento de um
sistema, conforme mostra a figura 1.1. Para que uma fase seja iniciada, é
necessário que a fase anterior seja totalmente concluída.
Mesmo com a execução rigorosa das fases de engenharia de requisitos e de
análise, a experiência na construção de sistemas mostra que ainda existe uma
lacuna de informações a serem especificadas para prosseguir com a fase de projeto.
Isto significa que especificar e modelar o que o sistema deve fazer não é suficiente
para saber como o sistema deve ser estruturado e organizado para satisfazer os
requisitos funcionais e os atributos de qualidade.

Figura 1.1: Processo de desenvolvimento em cascata

A figura 1.2 mostra o ponto onde as atividades da arquitetura são mais visíveis
dentro do processo de desenvolvimento de sistemas. A diferença entre as fases de
análise e de projeto e as atividades de arquitetura só são evidentes quando se trata
de sistemas grandes e complexos.
Para sistemas pequenos, a transição entre as fases de engenharia de
requisitos, análise e projeto é tranqüila, pois os modelos gerados nestas fases são
suficientes para representar os requisitos necessários, satisfazendo a construção do

19
20

sistema.

Figura 1.2: pontos onde as atividades de arquitetura são mais visíveis

Segundo [JAZ2000], muitos engenheiros acreditam que a arquitetura é


determinada pelos requisitos e por isso esperam que a fase de engenharia dos
requisitos esteja finalizada para então iniciar a definição da arquitetura. Porém,
apenas uma fração dos requisitos específicos do sistema tem influência na
arquitetura. A identificação dos requisitos que são significantes para a arquitetura
pode ser respondida através de um framework conceitual desenvolvido
especialmente para um domínio específico, uma vez que esta resposta é muito
dependente do domínio.

1.3. Princípios de arquiteturas

Para arquitetos de sistema, toda e qualquer técnica de decompor (dividir um


objeto grande em porções menores) sistemas de software abordam interesses
principais:
• A maioria dos sistemas é muito complexa para ser compreendida em sua
totalidade.
• Audiências diferentes exigem perspectivas diferentes de um sistema.
O desacoplamento ocorre dividindo o sistema em camadas. Estas são
sistemas em si mesmos e fazem o que todos eles fazem: obtêm uma entrada a partir
de seu ambiente e fornecem uma saída para o mesmo. A figura 1.3 mostra uma
representação das camadas arquitetônicas de um sistema.

20
21

Figura 1.3: Camadas arquitetônicas de um sistema. Adaptado de [JAZ2000]

A figura 1.3 mostra um típico sistema multicamadas. Para que realmente se


tenha êxito em separar os sistemas em camadas, deve-se prever que as classes e
objetos de uma camada devem depender estritamente de sua própria camada,
evitando, com isso um sistema do tipo “espaguete”, os quais fazem com que
pequenas alterações em uma camada se propaguem para praticamente todo o
“cetim”.
A arquitetura de três níveis de software surgiu na década de 1990 para superar
as limitações das arquiteturas de dois níveis cliente-servidor. A terceira camada
reside entre a camada cliente e a camada de dados. Essa camada fornece o
gerenciamento de processos em que a lógica de negócios é executada, e pode
acomodar milhares de usuários (em comparação a centenas de usuários
comportados na arquitetura de dois níveis) fornecendo serviços como conexão a
filas de mensagens, localização transparente, pool de conexões de banco de dados
e mais.
A arquitetura de N camadas é a mesma que a arquitetura de três camadas
exceto pelo fato de se ter mais de um servidor de aplicação atuando como camada
de regra de negócio de banco de dados. Essa arquitetura deve ser definida
cuidadosamente devido a sua grande complexidade.
Em um ambiente multicamada percebe-se algumas características, segundo
[ALL2003], que a camada cliente implementa somente a lógica de apresentação
(cliente magro). A lógica de negócios reside em uma camada independente,
tipicamente em um servidor de aplicação. A camada de acesso a dados fica também

21
22

em uma camada específica. As três camadas a seguir definem uma arquitetura


multicamada.
• Um componente de front-end6 capaz de fornecer lógica de apresentação
de uma forma portável. Como exemplo pode-se citar uma camada Web
ou uma camada de dispositivo móvel, como um celular.
• Um componente de back-end7, capaz de fornecer acesso a serviços
dedicados como servidor de banco de dados, aplicações legadas,
diretórios de domínios, etc.
• Um ou mais componentes de camada intermediárias, que permitem ao
desenvolvedor compartilhar a lógica de negócios isolando-a do sistema
real tipicamente em um servidor de aplicação.
Dentre as vantagens que se pode alcançar em sistemas multicamadas
podemos citar:
• As alterações na lógica de apresentação ou na lógica de negócios são
totalmente isoladas permitindo uma maior flexibilidade na hora de
desenvolver novos requisitos.
• Quando se deve alterar uma regra de negócio, a camada de
apresentação não deverá ser atualizada, diferentemente na arquitetura
de duas camadas (cliente-servidor) onde cada alteração na camada
servidora (tipicamente um banco de dados) deveria ser refletida na
camada cliente.
• O Cliente é isolado da origem de dados e de operações como rede, o
cliente pode acessar facilmente os dados sem saber onde estão e como
recuperá-los.
• As conexões com bancos de dados podem ser organizadas em filas para
prover uma maior performance, por isso se reduz drasticamente o tempo
para licenciar o usuário ao banco de dados.

1.3.1. Capacidades de uma arquitetura

Segundo [ALL2003], capacidades de uma arquitetura são as qualidades não-

6
Camada de uma aplicação que interage com o usuário.
7
Camada de armazenamento de dados em uma arquitetura de N camadas.

22
23

funcionais da mesma, incluindo escalabilidade, gerenciabilidade, desempenho,


disponibilidade, confiabilidade e segurança. As medidas de qualidade de um sistema
normalmente focalizam características de desempenho sob estudo. A seguir são
apresentados alguns conceitos importantes sobre capacidades arquiteturais.

Disponibilidade
Essa característica está sempre relacionada ao desempenho. A disponibilidade
é o grau em que um sistema é operável em um estado saudável. Matematicamente
a disponibilidade é um (1) menos a indisponibilidade. A disponibilidade é a relação
entre o tempo total em que uma unidade funcional é capaz de utilizar durante um
determinado intervalo e a extensão do intervalo. Um exemplo de disponibilidade é
100/168, se a unidade for capaz de ser utilizada por 100 horas em uma semana
[ALL2003].

Gerenciabilidade e Flexibilidade
Gerenciabilidade se refere ao conjunto de serviços que assegura a integridade
continuada, ou correção, da aplicação. Ela inclui segurança, controle de
concorrência e gerenciamento do servidor. Um exemplo métrico de gerenciabilidade
seria a quantidade de horas de pessoal no mês gastas para atualizações no
servidor.
Flexibilidade é a chave para uma aplicação disponível, confiável e escalonável.
É a capacidade de realizar mudanças arquiteturais para conceber novos requisitos
em uma maneira eficiente e não custosa. Tipicamente, uma aplicação flexível é uma
aplicação de fácil manutenibilidade. A separação do sistema em camadas aumenta
muito a flexibilidade de um sistema.

Desempenho
Atualmente, o desempenho é a capacidade de executar funções
suficientemente rápidas para atenderem aos objetivos. O tempo de resposta e a taxa
de resposta são importantes para uma aplicação.

Escalabilidade
Escalabilidade é a capacidade de suportar de uma maneira eficiente o aumento
da demanda de requisições sem afetar drasticamente a performance e com isso o
23
24

tempo de resposta. Pode-se prover a escalabilidade a um sistema de duas


maneiras:
! Verticalmente: é considerada a maneira mais fácil e barata para prover
escalabilidade, tipicamente adicionando mais memória e capacidade de
armazenamento para o servidor do sistema.
! Horizontalmente: consiste na adição de um ou mais servidores e, com isso,
prover uma forma de cluster para a aplicação. Esse tipo é bem mais custoso
e complexo.
Apesar de a escalabilidade vertical ser mais barata e simples de se
implementar, ela não é aconselhada para aplicações de missão critica, pois somente
adicionando capacidade de processamento não provê a tolerância a falha, ainda
com isso o sistema terá apenas um ponto de falha. Já com a adição de mais
servidores, pode-se prover um ambiente bem mais tolerante a falhas e eficiente,
podendo com isso também prover alguma forma de balanceamento de carga,
provendo o aumento da performance do sistema.

24
25

Capítulo 2

Padrões de Projetos e melhores práticas para projetos J2EE

Muito se ouve falar de padrões de projeto, pois o mundo do desenvolvimento


de Software, instantaneamente está passando a utilizar os padrões, seja por
influência de outras pessoas ou por estudos e necessidade, mas, devido a sua
grande ramificação surge um pergunta: “qual padrão de projeto utilizar?”.
Os padrões de projeto (design patterns) representam a idéia de evitar a “perda
de tempo” pensando em soluções a problemas que já foram anteriormente
enfrentados e solucionados. Atualmente, os projetistas de software raramente
solucionam problemas a partir de princípios elementares ou do início do processo.
Ao contrário, eles reutilizam soluções que já funcionaram anteriormente e as
adaptam para o problema em questão. É por isso que “os padrões de projetos têm
chamado a atenção e despertado o interesse dos projetistas de software, por
proporcionar elementos que conduzem ao reaproveitamento de soluções para
projetos, e não apenas a reutilização de código” [LEI2005].
Porém, a idéia de reutilizar soluções já anteriormente desenvolvidas não é
atual, tampouco surgiu com a então chamada “indústria de software”. Esta idéia
surgiu no final da década de 70, com a publicação de dois livros de Christopher
Alexander: “A Pattern Language” em 1977 e “A Timeless Way of Building” em 1979.
Nestes livros ele descrevia problemas e suas soluções para a arquitetura. Com isto,
ele descobriu que se ele procurasse estruturas que resolvessem problemas
similares, ele poderia discernir similaridades entre projetos de alta qualidade. Estas
similaridades foram chamadas por ele de “padrões”. [LEI2005].
Após isto, em 1995, Erich Gama, Richard Helm, Ralph Johnson e John
Vlissides se uniram e lançaram o livro “Design Patterns: Elements of Reusable
Object-Oriented Software”. Este livro descreve 23 padrões de projeto, divididos em
três categorias: padrões de criação, estruturais e de comportamento. Estes quatro
autores ficaram conhecidos como “a gangue dos quatro” (Gang of Four – GoF),
sendo os maiores incentivadores dos design patterns.
Em seu livro “A Times Way of Building”, lançado em 1979, Christopher
Alexandre fez a primeira definição de padrão de projeto: “Cada padrão é uma regra
de três partes, que expressa uma relação entre um determinado contexto, um

25
26

problema e uma solução”.


Já de acordo com [WIK2006], padrão de projeto é “uma solução geral para um
problema comum no projeto de software”. Os padrões de projeto não são uma
projeção final que será transpassada para o código do desenvolvedor de software.
Eles são soluções abstratas de como resolver determinados problemas do projeto
de software, independente de linguagem de programação.
Unindo as duas definições, pode-se dizer que para entender os padrões de
projeto é necessário entender suas três partes: o contexto, o problema e a solução,
sendo possível ter a solução recorrente para um problema em um contexto, mesmo
que em projetos distintos (como citado anteriormente, linguagens de programação
diferentes, por exemplo).

2.1. Padrões GoF

Como mostrado anteriormente, esses padrões surgiram quando Erich Gama,


Richard Helm, Ralph Johnson e John Vlissides se uniram e lançaram um livro
descrevendo 23 padrões de projeto.
Estes padrões são divididos em três categorias: Patterns Criacionais, Patterns
Estruturais e Patterns Comportamentais.
• Patterns Criacionais: Essa categoria possui os patterns que são utilizados para
criar objetos, ou seja, os patterns desta categoria devem ser utilizados no
momento em que uma classe é instanciada.
Cinco padrões são descritos por esta categoria: Abstract Factory, Builder,
Factory Method, Prototype e Singleton.
• Patterns Estruturais: Esta categoria possui patterns que dizem respeito à
composição das classes e dos objetos.
Sete padrões são descritos por esta categoria: Adapter, Bridge, Composite,
Decorator, Facade, Flyweight e Proxy.
• Patterns Comportamentais: Esta categoria possui patterns que dizem respeito à
interação e responsabilidade de objetos, ou seja, estes patterns ajudam a tornar
um comportamento complexo gerenciável.
Onze padrões são descritos por esta categoria: Chain of Responsability,
Command, Interpreter, Iterator, Mediator, Memento, Observer, State, Strategy,

26
27

Template Method e Visitor.


Não entraremos em detalhes com relação aos padrões GoF, devido eles não
serem relevantes a este trabalho.

2.2. Padrões J2EE

Assim como para os padrões GoF, o J2EE apresenta uma divisão em seus
padrões. Eles são divididos em três categorias, de acordo com camada [ALL2003]:
Patterns da Camada de Apresentação, Patterns da Camada de Negócios e Patterns
da Camada de Integração.

2.2.1. Patterns da camada de apresentação


A camada de apresentação é responsável por encapsular a lógica relacionada à
interface do usuário. Os padrões desta camada interceptam as requisições vindas
do (e para o) usuário, para que toda a lógica fique transparente para o mesmo. Há
sete padrões descritos por esta camada [ALL2003]:
• Composite View: Este padrão é utilizado quando se deseja construir um
componente de visualização através de subcomponentes de visualização, com
o objetivo de diminuir as responsabilidades e permitir o reuso dos
componentes.
• Dispatcher View: Este patterns tem por objetivo combinar padrões de
apresentação e encapsular a lógica de navegação.
• Front Controller: Este padrão é utilizado quando se precisa centralizar o
processamento de requisições para tratamento da solicitação do cliente. Além
disso, ele também permite criar uma interface genérica com o intuito de delegar
o processamento do controlador para os componentes auxiliares.
• Intercepting Filter: O objetivo do Intercepting Filter é permitir o pré e o pós-
processamento de uma requisição do cliente, além de permitir um
processamento centralizado e compartilhado através de requisições.
• Service to Worker: Este padrão tem o mesmo objetivo do Dispatcher View,
com a diferença de que ele realiza mais processamento antes de despachar a
requisição, enquanto que o Dispatcher View realiza mais processamentos
depois de despachar a requisição.

27
28

• View Helper: Tem por objetivo separar o código da interface do usuário do


processamento de dados necessários à construção da View. É utilizado
quando a aplicação precisa encapsular a lógica da formatação de dados
relacionada à apresentação.
• Model View Controller (MVC): Este padrão fornece uma maneira de dividir a
funcionalidade envolvida na manutenção e apresentação dos dados de uma
aplicação. Um sistema MVC é dividido em um modelo de dados, um conjunto
de visões e um conjunto de controladores. As visões fornecem a interface do
usuário e, em uma aplicação padrão, consistem de JSP, HTML, XML, ASP, etc.
Esta camada de visão é responsável por receber os dados fornecidos pelo
usuário e apresentar os resultados. O controlador é geralmente implementado
como um ou mais servlets Java. Ele controla e mapeia as ações do usuário e
da camada de modelo. O modelo, por sua vez, representa os dados e o
comportamento da lógica de negócios, além de encapsular, manipular e gerar
os dados.
A principal função do MVC é a divisão de responsabilidades. Por exemplo, uma
visão (view) pode usar o modelo (model) para exibir resultados de determinada
consulta ao banco de dados (também feita pelo modelo), mas a visão não é
responsável por atualizar o banco de dados. O controlador define o
comportamento da aplicação, é ele que interpreta as ações do usuário e as
mapeia para chamadas do modelo. A lógica de negócio pode ser representada
tanto pela camada de modelo como pela camada de controle (controller). Um
exemplo de framework que implementa o padrão MVC é o Struts.
A principal vantagem do MVC é que o desenvolvimento de aplicações que o
implementem pode se tornar mais simples e rápido, pois com a divisão das
camadas, pode-se desenvolver paralelamente as três camadas. Porém, vale
ressaltar que ele não é aconselhável para projetos pequenos, pois pode tornar
o projeto mais demorado, devido requerer uma maior quantidade de tempo
para análise e modelagem do sistema [SIE2004].

2.2.2. Patterns de camada de negócios


A camada de negócios encapsula a lógica central da aplicação, fornecendo
serviços requeridos pelos clientes. Uma maneira de se implementar o
processamento de negócios é através do uso de Enterprise Java Beans (EJB). Oito
padrões são descritos por esta camada [SIE2004]:
28
29

• Business Delegate: Este padrão é utilizado para reduzir o acoplamento entre


os clientes da camada de apresentação e os serviços de negócios. O Business
Delegate oculta os detalhes de implementação por trás do serviço de negócios,
como detalhes de pesquisa e acesso da arquitetura EJB. Este padrão acaba
por funcionar como proxy ou fachada para cada Session Bean (conceituado no
capítulo 3). Além disso, ele serve para diminuir o acoplamento entre as
camadas.
Para ocultar estes detalhes, o Business Delegate utiliza o Lookup Service,
que é um componente que pode ser implementado tanto como parte do
Business Delegate como separadamente.
Há duas estratégias de implementação do Business Delegate:
• Delegate Proxy Strategy: Nesta estratégia, o Business Delegate pode
armazenar os serviços de negócios em cache para os clientes e também
disponibiliza uma interface simples.
• Delegate Adapter Strategy: Nesta estratégia, o Business Delegate permite a
comunicação com sistemas heterogêneos.
• Composite Entity: Este padrão tem como objetivo modelar, gerenciar e
representar um conjunto de objetos persistentes relacionados, ao invés de
representar como objetos distintos. Para isso, os Entity Beans devem modelar
apenas os objetos principais, e para melhorar a performance, a chamada dos
entity beans para os seus objetos não deve ser interceptada pelo container.
• Service Locator: Este padrão é utilizado quando se precisa fornecer um
método uniforme para pesquisa e criação de serviço, também quando se
deseja ocultar a complexidade da pesquisa de componente do EJB. Além
disso, ele pode ser usado para abstrair o uso de JNDI (Java Naming and
Directory Interface, que é uma API J2EE que fornece uma interface padrão
para localizar usuários, máquinas, objetos, redes e serviços).
• Session Façade: Este padrão é responsável por acessar recursos, atualizar
dados e realizar a transação com uma interação limitada com o cliente. O seu
objetivo é fornecer uma interface simples e única para todos os clientes.
Também é utilizado para se reduzir o tráfego de rede entre os clientes e seus
componentes EJB.
• Transfer Object: Este padrão é utilizado quando se precisa obter vários dados
de um determinado objeto. Ele reduz a quantidade de requisições feitas para
recuperar estes dados, reduzindo, desta forma, o tráfego na rede e a
29
30

duplicação de código.
• Transfer Object Assembler: O objetivo deste padrão é construir um modelo
de dados requerido pelo cliente, utilizando vários Transfer Objects para
recuperar os dados dos objetos necessários ao modelo que o cliente deseja.
Ele acaba por reduzir o acoplamento existente entre os clientes e o modelo da
aplicação, além de melhorar a performance da rede.
• Value List Handler: Deve-se utilizar este padrão quando se quer ter controle
sobre a busca, e precisa fornecer recursos de processamento de listas, e
também se deseja fazer cache dos resultados do lado do servidor. Com isso,
ela melhora a performance da rede, assim como em outros padrões.
• Value Object: Este padrão surgiu com o intuito de minimizar o número de
chamadas remotas que são necessárias quando está usando EJB, ou seja,
serve para encapsular os dados de negócios.

2.2.3. Patterns de camada de Integração


Esta camada encapsula a lógica relacionada com a integração do sistema com a
camada de informação distribuída. Os padrões pertencentes a esta categoria são
responsáveis por acessar recursos e sistemas externos. Normalmente essa camada
fica acoplada com a camada de negócios. Apenas dois padrões pertencem a esta
categoria:
• Service Activator: O principal objetivo deste padrão é receber e processar
mensagens assíncronas do cliente. Ele recebe as mensagens e as repassa
para os métodos correspondentes a atender a requisição.
• Data Access Object (DAO): Este é um dos padrões mais utilizados, o seu
objetivo é abstrair e encapsular todo o acesso a uma fonte de dados. Existem
várias formas de se acessar dados, como através de banco de dados, ou
através de um arquivo XML, por exemplo. Se o desenvolvedor for colocar o
acesso ao dado diretamente no código, ele irá ter que modificar uma grande
quantidade de código caso o modo de acesso a dados modifique, isto é, haverá
um grande acoplamento entre a lógica de negócio e a implementação do
acesso a dados.
O DAO oferece uma interface comum a todos os tipos de acesso a dados. Um
componente de negócio fica exposto apenas a esta interface, que esconde
toda a complexidade relativa à interação com a fonte de dados. Como a
interface de um DAO não se altera quando sua implementação precisa ser
30
31

modificada, este padrão permite alterar a fonte de dados sendo utilizada numa
aplicação sem afetar os componentes de negócios que fazem uso deste.
O uso deste padrão resulta na transparência da aplicação quanto a fonte de
dados, facilita a migração de uma fonte de dados a outra, além de criar uma
nova camada, onde ficará centralizado o acesso a dados.

2.3. Benefícios e Problemas do uso de Padrões de Projeto

Sem dúvidas, os padrões de projeto surgiram com o intuito de facilitar o


processo de desenvolvimento de software. O uso dos padrões trouxe inúmeros
benefícios para o desenvolvedor. Porém, o mau uso dos mesmos pode acarretar em
problemas.
De acordo com [MAI2005], os padrões de projeto possuem dois principais
benefícios: o uso de padrões de projeto auxilia o desenvolvedor a solucionar
problemas que já foram solucionados anteriormente por outras pessoas, além tornar
a manutenção e o entendimento do sistema mais simples. Outro benefício apontado
por [MAI2005] é que o uso de padrões torna a comunicação entre os
desenvolvedores mais eficiente. Os desenvolvedores podem solucionar algum
problema de outro desenvolvedor mais rapidamente se ele já conhecer algum
padrão de projeto que solucionou algum problema semelhante anteriormente. Na
verdade, um benefício complementa o outro.
Além disso, o uso de padrões torna o sistema mais fácil de entender e de
manter, facilitando o desenvolvimento de módulos coesos [MAC2005].
Em suma, alguns dos principais benefícios dos padrões são: maior
flexibilidade, fácil manutenção, pode ser aplicado a diversas tecnologias, fornecem
um vocabulário comum entre o software e os desenvolvedores, além de enfatizar e
apoiar a reutilização de código.
O principal problema existente no uso de padrões de projeto é o mau uso dos
mesmos. Utilizar padrões de projeto em um projeto muito pequeno pode, ao invés de
facilitar o desenvolvimento, dificultá-lo, tornando o código-fonte grande e complexo.
Isto porque o objetivo do uso dos padrões é o de facilitar o processo de
desenvolvimento de sistemas grandes.

31
32

Em contradição, existem os chamados antipatterns, que, ao invés de aumentar


a qualidade do software, as limita. Por exemplo, é muito comum, numa aplicação
web, as páginas se referenciarem diretamente [LOZ2003]. Com o crescimento da
aplicação, isto dificulta a manutenção, pois a simples alteração de um nome pode
resultar em dezenas de alterações em diversos documentos. Estas falhas, ou
"soluções negativas" são valiosas, ao passo que se pode aprender a não repetir
estas falhas, através do estudo aprofundado, descobrindo suas causas.
Com a experiência que os desenvolvedores vão adquirindo com o tempo,
acabam incorporando práticas e vícios que prejudicam o entendimento, a
manutenção e a performance dos sistemas que desenvolvem.
Ao contrário dos padrões de projeto, o antipattern começa com uma “solução
ruim” já existente, possivelmente resultada de uma inexperiência em se resolver um
tipo de problema em particular ou na aplicação de um padrão de projeto em um
contexto errado. O AntiPattern então enfatiza esta solução, de modo que permita o
reconhecimento da mesma, seus sintomas e conseqüências. O Antipattern então
apresenta uma nova solução que refatora o sistema visando maximizar os
benefícios e minimizar as conseqüências.

32
33

Capítulo 3

Arquiteturas J2EE

Com o avanço de aplicações voltadas para a Internet, foram necessárias


aplicações que necessitavam ser escaláveis e ininterruptas. A plataforma J2EE
assegura esses requisitos sendo um conjunto de especificações que permitem um
desenvolvimento de aplicações em N camadas, providenciando além de um
conjunto de APIs, uma infra-estrutura de runtime para hospedar a aplicação.
A arquitetura J2EE é dividida em quatro containers, sendo: o container de
aplicação cliente, o container de applets, o container de desenvolvimento web e o
container de negócios chamado de container de Enterprise Java Beans.
Como mostrado no capítulo 1, sobre arquiteturas separadas em camadas,
percebe-se que para aumentar a escalabilidade e diminuir acoplamento de uma
arquitetura complexa, é necessário projetar cada uma dessas camadas e prover
uma desacoplada forma de integração entre elas.

3.1. Container WEB em J2EE

Para a execução de aplicações dinâmicas para a Web, a plataforma J2EE


disponibiliza o conceito de contêiner web que podem pertencer a dois grupos:
[SUN2006].
• Orientada a apresentação: uma aplicação web orientada a apresentação
gera uma interativa forma de resposta ao cliente, usando alguma forma de
linguagem de marcação, como o HTML e o XML, e uma resposta dinâmica
para os clientes.
• Orientada a Serviço. Em aplicações orientadas a serviços, existe uma
implementação de endpoint para um Serviço Web. As aplicações orientadas
a apresentação são, em sua maioria, os clientes de aplicações orientados a
serviço.
Em suma, os componentes web podem ser implementados tanto usando
tecnologias Java Servlet quanto Java Server Pages, ou endpoints de serviços. A
interação entre um cliente e o servidor web está ilustrado na figura 3.1.

33
34

Figura 3.1: Interação entre cliente e servidor Web. Adaptado de [JAZ2000]

Como mostra a figura 3.1, o cliente manda um HTTPRequest (1.) para o


servidor web. Essa requisição é recebida pelo Web Server, que encapsula a
requisição em um objeto chamado HTTPServletRequest e entrega-o a um objeto
servlet (2.) que interage com JavaBeans, com banco de dados ou outro
componente web pra que ele possa gerar a resposta dinâmica para o usuário.
O componente web pode gerar objetos chamados HTTPServletResponse (5.)
ou pode passar a requisição para outro componente que então gere a resposta. O
servidor, então, converte esse objeto em uma resposta simples HTTP e retorna para
o cliente. [SUN2006]

3.2. EJB e o modelo de container

Após discutir rapidamente a camada web de servidores J2EE, é importante


discutir também a camada de negócios sugerida pela especificação J2EE. Esta
camada é implementada usando componentes de negócios distribuídos chamados
de Enterprise Java Beans.
Com a necessidade de se desenvolver soluções distribuídas objetivando
atender um principio básico de qualidade de serviço, o da tolerância à falhas,

34
35

iniciaram-se vários esforços para seu desenvolvimento. Na plataforma Java, o


desenvolvedor trabalha de uma maneira distribuída através de RMI8 (Remote
Method Invocation). O RMI atua como um RPC9 (Remote Procedure Call), porém de
uma forma orientada a objetos, podendo transportar além de simples caracteres,
objetos complexos.
Entretanto, percebeu-se que desenvolvendo aplicações distribuídas usando
RMI era extremamente desgastante, o desenvolvedor se ocupava mais com
problemas de comunicação da rede, problemas de segurança, administração da
performance e da propagação de transações do que as regras de negócio do próprio
sistema.
Diante desses problemas, foram elaborados os componentes de negócios da
arquitetura J2EE para abstrair todos os problemas referentes à infra-estrutura dos
desenvolvedores, fazendo com que eles apenas se preocupassem com a regra de
negócio propriamente dita.
Os EJBs podem ser conceituados como sendo componentes de negócios que
habitam sempre um servidor de aplicação e que o desenvolvedor tem a obrigação
apenas de desenvolvê-lo, outros recursos burocráticos são tarefas do servidor de
aplicação. Dentre esses recursos abstratos do desenvolvedor podemos citar:
• Localização de recursos: O Servidor de aplicação oferece uma
plataforma para que os EJBs descubram serviços hospedados no servidor
de aplicação através de JNDI10, esses serviços podem ser uma conexão a
uma fonte de dados, a uma fila de mensagens assíncronas, a outros EJBs,
a um Mainframe, a fontes LDAP, etc.
• Segurança: a segurança em aplicações J2EE é implementada no servidor
de aplicação, significando que o desenvolvedor deve declarar quais
recursos são visíveis e accessíveis. Chamadas não autorizadas a
componentes de negócios ou a páginas web podem ser interceptadas
bastando ao desenvolvedor definir os perfis e o acesso aos mesmos.
• Performance: um grande fator na adoção de servidores de aplicação é
quanto ao fator escalabilidade. Para um servidor de aplicação ser
considerado um servidor J2EE, ele deve implementar no mínimo as regras
8
Forma prática de se implementar um Sistema Distribuído em plataforma Java.
9
Tipo de protocolo para chamada remota de procedimentos em qualquer lugar da rede ou uma chamada de
função para o método de transferência de controle de parte de um processo para outra, permite a divisão de um
software em várias partes, compartilhamento de arquivos e diretórios.
10
API para acesso a serviços de diretórios. Permite que aplicações cliente descubram e obtenham dados ou
objetos através de um nome

35
36

inclusas na especificação J2EE. Dentre essas regras, pode-se citar o


gerenciamento do ciclo de vida dos EJBs.
• Controle Transacional: em aplicações extremamente complexas, o
quesito “transação” não faz parte apenas do banco de dados. Com isso,
deve-se proporcionar uma maneira simples e eficaz de tratar transações, e
o servidor de aplicação atua como o implementador baixo nível,
fornecendo ao desenvolvedor EJB um nível amigável de utilização das
mesmas.
• Persistência: Através do EJB CMP, que será discutido posteriormente, o
servidor de aplicação pode gerenciar a forma como seus objetos de
domínio serão armazenados em algum meio persistente, dando total
abstração para o desenvolvedor e causando uma independência de
origem de dados.

3.3. Performance com EJB

Performance é um tópico essencial para qualquer aplicação que necessite ter


um número satisfatório de usuários. Com o crescimento do uso da Internet para
diversos fins, a demanda para aplicações com alta performance foi aumentando
consideravelmente. Porém, não faz sentido termos somente performance para um
número pequeno de usuários. O que aconteceria de esse numero de usuários
dobrasse rapidamente e em seguida triplicasse? Será que o desempenho seria o
mesmo do início do processo? Será que o tempo de resposta seria aceitável? É
neste ponto que entra o conceito de escalabilidade.
Como visto no capítulo 1, escalabilidade é “a capacidade de suportar de uma
maneira eficiente o aumento da demanda de requisições sem afetar drasticamente a
performance”. Com isso, para que o sistema possa ser escalável, seu aumento de
carga não pode comprometer muito o seu desempenho. Tendo esse conceito em
mente, os servidores de aplicação gerenciam o ciclo de vida de seus componentes
EJB para prover um melhor gerenciamento de recursos refletindo diretamente em
um tempo de resposta ainda mais aceitável.
O ciclo de vida de um componente EJB não é administrado por seu
desenvolvedor, e sim pelo servidor de aplicação. Mas, por quê? Como o processo

36
37

de criação e manutenção de um EJB é uma tarefa dispendiosa de recursos, o


servidor de aplicação aplica certas regras para disponibilizá-los aos clientes.
A primeira etapa consiste na criação dos EJBs. Muitos desenvolvedores acham
que o EJB somente é criado quando algum usuário o invoca, entretanto, percebe-se
que isso não está correto, por motivos de , tipicamente ao se iniciar o servidor de
aplicação instancia-se algumas dezenas ou centenas de EJBs e os colocam em um
pool de EJBs.
A escalabilidade provém justamente do pool de EJBs, que por sua vez é uma
fila, repositório contendo instâncias de EJBs prontas para serem usadas, e no caso
de EJBs sem estados, discutidos posteriormente, quando um usuário solicita um
EJB, o container o retira do pool e entrega para ser usado. Quando o cliente não
precisa mais de seus serviços, ao invés dele ser descartado, ele retorna para o pool
causando com isso uma grande velocidade de resposta, pois não há o gasto de
criação e destruição sempre na aplicação. O container também é responsável por
retirar ou não do pool de EJBs. Caso o sistema esteja com poucos recursos, uma
boa técnica é retornar do pool alguns EJBs não usados recentemente, através, por
exemplo, do algoritmo de LRU, que consiste em escolher menos usado
recentemente.
Além de prover um pool para EJBs, o servidor de aplicação deve também
fornecer um pool de conexão para origens de dados, pois é extremante dispendioso
cada cliente criar uma conexão direta com o banco de dados, o que certamente
deixaria o sistema inacessível com uma quantidade razoavelmente de usuários.
Com um pool de conexões de dados, as conexões ficam no pool até que um cliente
a requisite. Se vários clientes acessarem o pool para recuperar uma conexão e não
existir uma disponível, o cliente fica em uma fila de espera aguardando que algum
recurso seja liberado.
Tais técnicas permitem afirmar que a plataforma J2EE é escalável. Porém,
para aumentar a performance de sistemas existem duas opções: Escalabilidade
Horizontal e Escalabilidade Vertical.
Para prover uma escalabilidade horizontal em um sistema, é necessário
adicionar máquinas à aplicação, tipicamente colocando-as em cluster, com isso
aumentará o nível de complexidade e gerenciabilidade do sistema, mas provê uma
grande melhoria na tolerância à falhas, pois se uma máquina ficar inacessível o
sistema rodará normalmente na outra, além de prover um balanceamento de carga.
O balanceamento de carga funciona interceptando as requisições aos
37
38

servidores e verificando como está a carga do possível alvo. Se essa máquina


estiver com carga máxima, então se deve redirecionar a requisição para a máquina
seguinte.
Para uma escalabilidade vertical o processo é bem mais simples, pois consiste
em adicionar componentes de hardware na máquina atual a fim de aumentar seu
poder de processamento. Esta técnica é bem mais barata que a horizontal e
praticamente não afeta a aplicação, porém, com isso, o sistema tem apenas um
ponto de falha e se essa maquina parar, a aplicação ficará indisponível.

3.4. Tipos de EJB

Segundo [ROM2004], pode-se dividir os componentes EJBs em três tipos: os


de sessão, os de entidade e os dirigidos por mensagens. Neste trabalho, iremos
explorar também seus subtipos: stateless session beans (componentes de sessão
sem estado), statefull session beans (componentes sessão com estado), container
manager persistence (persistência gerenciada pelo container), bean manager
persistence (persistência gerenciada pelo bean).

3.4.1. Session Beans


Os sessions beans representam o trabalho sendo realizado pelo cliente que o
chama. Eles são objetos de processo de negócios que implementam as regras de
negócio, algoritmos ou workflows. Por exemplo, os session beans podem ser usados
para processamento de venda, transações bancárias, operações de banco de
dados, cálculos complexos, etc. Em suma, eles são componentes reusáveis que
implementam lógicas de negócios. [ROM2004]
Uma grande diferença entre os session beans e os outros tipos de EJBs está
em seu ciclo de vida. Geralmente eles têm um ciclo de vida curto, que normalmente
é o tempo de sessão que um cliente utiliza. As instâncias de session beans não são
compartilhados com outros clientes.
Todos os EJBs possuem um grau de conversação com algum tipo de cliente.
Pode-se dizer que conversação é a interação entre o cliente e o componente, e
entre os métodos subseqüentes e o cliente. O tipo de conversação é o fator
determinante para separar os dois tipos de EJBs, com estado conversacional e sem
38
39

estado conversacional.
Um session bean representa o trabalho realizado por um único cliente. Esse
trabalho pode ser realizado dentro de uma única invocação de método ou pode
englobar diversas invocações de métodos. Se o trabalho abranger mais de um
método, deve-se reter o estado do objeto do usuário por todas as chamadas
subseqüentes. Portanto um bean de sessão com estado é requerido [ALL2003].
Um típico exemplo de bean com estado conversacional é uma aplicação de e-
commerce que possui um carrinho de compras. Ao longo da navegação, o cliente
pode adicionar ou remover itens no carrinho. Tal lógica poderia ser implementada
usando statefull session beans.
Naturalmente os processos de negócios são realizados usando apenas uma
simples e única requisição de conversação com o cliente. Como visto anteriormente,
uma simples chamada ao método de negócio pelo cliente não requer estado
conversacional. Como o processo é concebido através de apenas um método, o
container que o gerencia normalmente após a chamada do método o bean pode ser
destruído, pode ser recriado ou pode, na maioria das vezes, voltar para o pool de
beans. Um exemplo de uso de beans sem estado é um componente que realiza uma
consulta em algum ambiente de armazenamento de dados e o retorna para o cliente,
o cliente somente faz uma chamada e o componente o devolve a resposta ao
cliente.
Na figura 3.2 pode-se ter uma idéia do modelo de pool de EJBs de sessão.

39
40

Figura 3.2: Modelo de Pools de EJB de sessão. Adaptado


de [JAZ2000]

3.4.2. Entity Bean


Uma das grandes vantagens de EJB é o poder de se usar entity beans. Entity
Beans são objetos persistentes que são armazenados em algum meio persistente,
normalmente um banco de dados, significando que se pode modelar objetos de
negócio fundamentais com entity beans.
Uma clássica maneira de se armazenar objetos Java é através do uso de
banco de dados relacionais, como MySQL, Oracle, SyBase, entre outros. Ao invés
de serializar o objeto em arquivos binários, pode-se decompor o objeto em partes e
armazenar cada parte separadamente. Por exemplo, um objeto Aluno possui
atributos nome e matrícula. Quando cada atributo do objeto é armazenado, ele é
armazenado em um campo separado. Normalmente, quando armazena objetos
Java, usa-se a API JDBC [JDB2006] para mapear o objeto ao banco de dados
relacional. Para recuperar objetos armazenados, o processo é parecido.
Normalmente se recupera as linhas resultantes da consulta e mapeia-se de volta
para objetos Java.
O processo de mapear objetos a banco de dados relacional chama-se
Mapeamento Objeto-relacional [ROM2004]. O mapeamento objeto-relacional pode
ser realizado de duas maneiras: diretamente dentro do código usando a API padrão
JDBC ou através de algum tipo de produto externo como TopLink, Hibernate, IBatis,
dentre outros. Esses produtos têm alcançado uma enorme popularidade, devido sua
facilidade e produtividade, ao contrário do mapeamento direto usando JDBC.

40
41

Foi mostrado na seção anterior que os EJBs de sessão são modelados para
realizar regras de negócio, algoritmos, serviços para o cliente. Do outro lado, cada
aplicação necessita ter seus objetos de modelo persistidos. É nesse contexto que
entra a tecnologia dos entities beans.
Beans de Entidade modelam conceitos de negócios que podem ser expressos
por nomes e esta é uma regra importante quando na sua modelagem de sistema o
desenvolvedor precisa eleger um candidato a ser um Bean de Entidade. Estes tipos
de beans modelam realmente um dado no banco de dados onde uma instância
representa uma linha na tabela de um banco de dados. A partir disto pode-se ter o
questionamento: Porque não acessar o banco de dados diretamente? Existem
muitas vantagens em se usar Beans de Entidade ao invés de acessar a base de
dados diretamente. Estes beans promovem um simples mecanismo de acesso e
alteração de dados. É muito mais fácil, por exemplo, executar um método para
atualizar um campo de um entity do que fazer um comando SQL11 para isto. Quando
um novo bean é criado, um novo registro deve ser inserido na base de dados e uma
instância do bean associada a este dado. Conforme o bean é usado e seu estado é
alterado, estas mudanças devem estar sincronizadas com a base de dados. Este
processo de coordenação dos dados do banco de dados com a instância do bean é
chamado de persistência.
Um bean de entidade pode ser considerado como de vida longa (long-lived)
porque sobrevive a um crash no servidor de aplicativos e por sua característica em
um modelo N-tier ficaria mais próximo do DBMS (Database Manager System).
Com relação aos beans de sessão, os beans de entidade são muito diferentes,
pois ao invés de se modelar um fluxo de controle (workflow), que é papel do session
bean, eles são responsáveis pelos dados de negócios (Core Business Data). Já com
relação ao mapeamento com as tabelas, um bean de entidade não necessita estar
mapeado para uma única tabela, ele pode ser representado por várias, devido na
fase de design nem toda tabela de seu esquema de persistência necessita estar
mapeada para um bean de entidade.

3.4.3. Message Driven Bean


Antes da versão 2.0 da especificação EJB, a maioria dos consumidores de
mensagens JMS (Java Message Service) foi construída como simples programas

11
sintaxe usada para a definição e manipulação de dados em um banco de dados relacional.

41
42

Java fora do container J2EE. Existia um programa Java que era ativado e conectado
a um destino JMS que ficava esperando as mensagens.
Embora a construção de consumidores de mensagens por meio de aplicações
Java fosse uma das melhores soluções para o problema, esta solução gerava vários
outros problemas e o maior seria a escalabilidade. Quando o número de mensagens
na fila aumentava, haveria a necessidade de se executar um programa Java multi-
threads para promover a escalabilidade da leitura destas mensagens, e se este
volume variasse muito, ficaria difícil fazer um algoritmo que, na medida exata,
consumiria as mensagens sem perda de performance com muitas threads e sem
perda destas mensagens no caso de poucas threads.
Baseados nisto, alguns vendedores de EJB pensaram nesta funcionalidade na
versão 1.1. Porém, a especificação não definia este tipo de bean, o que fazia com
que cada vendedor tivesse um padrão.
Basicamente, beans orientados a mensagens processam alguma lógica de
negócios usando mensagens JMS enviadas para uma destinação particular, ou seja,
consomem mensagens JMS através da tecnologia EJB.
A grande diferença entre beans orientados a mensagens e os beans de sessão
ou de entidade é que os beans orientados a mensagens não possuem interfaces
home nem interface remote. Então, podemos deduzir que estes beans são
completamente escondidos do cliente. O único meio dos clientes comunicarem com
os beans orientados a mensagens seria enviando uma mensagem para um
destinatário JMS. A figura 3.3. demonstra o funcionamento dos beans orientados a
mensagens.

Figura 3.3: Funcionamento de um Message Driven Bean

42
43

Como se pôde verificar, um cliente envia uma mensagem JMS para um destino
e o container passa esta mensagem JMS para uma instância do bean orientado a
mensagens que tem se registrado como um receptor de mensagens para uma
destinação particular. Por usar um pool de instância de beans, o container é capaz
de manipular as mensagens que chegam de uma forma muito mais eficiente e
aumenta a escalabilidade das operações JMS. As instâncias dos beans podem ser
colocadas ou retiradas do pool dependendo das necessidades do container de
atender as requisições.

43
44

Capítulo 4

Aplicando a tecnologia J2EE e seus problemas

4.1. Quando aplicações distribuídas são apropriadas?

Uma das grandes decisões arquitetônicas é se a aplicação deve ser distribuída


ou se todos os componentes devem ficar em um único servidor rodando a aplicação.
Os autores acreditam que existe certa tendência em aplicar a plataforma
distribuída de J2EE na maioria dos projetos sem realmente estudá-los a fundo antes
da escolha arquitetônica. Esta é uma escolha potencialmente custosa, pois não se
deve escolher uma arquitetura distribuída sem ter realmente bons motivos para tal.
De acordo com [JOH2004], abaixo seguem algumas principais vantagens em se
usar arquiteturas distribuídas:
• Suportar tecnologias clientes J2EE como aplicações Swing dando a ela
uma camada de negócio. Um container web J2EE provê uma camada
intermediária para aplicações que possuem o browser como cliente. Com
isso, esse argumento não se aplica para aplicações com apenas interface
web.
• Quando aplicações necessitam integrar recursos distribuídos em uma
empresa. Como uma aplicação envolvendo vários containeres de EJB,
cada um acessando um recurso como fonte de dados ou aplicações
legadas inacessível em outro lugar na rede.
• Para se ganhar controle sobre onde cada componente distribuído está
instalado, aumentando com isso a escalabilidade e a confiabilidade.
• Em casos raros, deve-se escolher uma aplicação distribuída para se
adicionar um firewall entre o servidor web e o servidor de regras de
negócio.
Se a primeira ou segunda afirmação for válida, uma arquitetura distribuída
baseada em EJB com interfaces remotas é a solução mais simples e ideal.

4.1.1. Aplicações Distribuídas e Escalabilidade


O J2EE e desenvolvedores tendem a assumir que aplicações distribuídas
oferecem uma escalabilidade inigualável. Entretanto isso pode ser questionado.

44
45

Soluções com apenas uma máquina virtual Java (JVM) tem uma performance
mais alta que aplicações distribuídas, devido ao fato da latência de invocações
remotas. Além disso, aplicações com JVM únicas podem ser clusterizadas
facilmente. Containeres web de alta qualidade oferecem funcionalidades de cluster,
esse não é um requisito apenas de containeres EJB.
Vantagens como balanceamento de carga de requisições HTTP pode ser
realizada por elementos do cluster oferecidos pelo servidor J2EE ou por
componentes como o Cisco Loader Balance. Usando o balanceamento de carga
através de hardware tem-se a vantagem de igual funcionamento em qualquer
servidor J2EE.
Apenas adicionando uma camada remota (como uma camada EJB) em uma
aplicação Web não necessariamente torna o sistema mais escalável. Uma aplicação
que não requer armazenamento de estado no servidor pode crescer linearmente e
indefinidamente sem o uso de chamadas remotas. Quando o armazenamento de
estado é requerido, a escalabilidade torna-se limitada, mesmo adotando uma
solução distribuída [JOH2004].
Se nós carregarmos o estado da aplicação no servidor web (através do objeto
HttpSession), a qualidade do suporte a cluster (roteamento e gerenciamento de
estado) do servidor web irá ditar a escalabilidade do sistema. Se carregarmos o
estado no container EJB, em statefull session beans, o container EJB terá uma
dificuldade semelhante. O EJB não pode ser considerado uma “bala da prata” que
faz com que o problema da replicação de estado se resolva tão facilmente.
Uma aplicação distribuída com interfaces remotas somente será mais escalável
que aplicações com máquina virtual única somente nesses casos:
• Os objetos acessados remotamente não possuem estado (stateless),
mesmo que o estado seja mantido na camada web.
No contexto de J2EE isso significa que as regras de negócios serão
implementadas usando stateless session beans.
• Muitos dos objetos de negócio consumem recursos que não podem ser
alocados em qualquer servidor ou em qualquer camada.
Nesses raros cenários, acoplando a camada web com a camada de
negócios tem-se uma limitação de escalabilidade comparada a
arquiteturas cliente-servidor.
• Os objetos de negócio realizam um trabalho bem superior que a camada

45
46

web
Nesse cenário será necessário rodar as regras de negócios em mais
containeres EJB.
Um modelo de objetos de negócio sem estado é considerado altamente
escalável, pois se pode adicionar quantos containeres EJBs quanto desejado, sem
aumentar com isso o número de containeres web ou o overhead de algum tipo de
replicação de estado que possa ser requerida na camada web. Entretanto, essa
arquitetura somente será mais escalável que uma arquitetura simples se a carga
entre a camada de negócio e a camada web for pequena [JOH2004].

4.1.2. Aplicações Distribuídas e Confiabilidade


As aplicações distribuídas, necessariamente são mais robustas que aplicações
simples (com um único servidor)? Novamente, arquitetos J2EE e desenvolvedores
tendem a assumir isso. Entretanto, esse conceito é questionável. Qualquer aplicação
com armazenamento de estado sofre do problema de server afinity, na qual cada
usuário é associado a um servidor em particular. Desenvolver uma aplicação
distribuída não soluciona esse problema.
Se a aplicação mantiver o estado no servidor, ela será referenciada na camada
web, na qual se tornará statefull. Se tivermos uma camada web com estado, a
aplicação, sendo distribuída ou não, poderá ser confiável dependendo diretamente
da tecnologia usada no cluster do servidor web. Se um usuário estiver associado a
um servidor e esse vir a quebrar, não importa se os objetos de negócio estiverem
em outro servidor que ainda esteja rodando. O usuário encontrará problemas da
mesma maneira, a menos que a sessão tenha sido replicada com sucesso. Com o
uso de statefull session beans, nós apenas movemos esse problema para o
container EJB. Segundo [JOH2004], os EJBs com estado tendem a ser menos
robustos que o tratamento de estado pelo objeto httpSession.
Somente se tivermos uma arquitetura na qual o armazenamento de estado seja
mantido na camada web e os objetos de negócio seja sem estado, a arquitetura
distribuída torna-se mais robusta e escalável que aplicações simples.
A figura 4.1 mostra uma arquitetura J2EE robusta e escalável.

46
47

Figura 4.1: Arquitetura J2EE robusta e escalável. Adaptado de [JOH2004]

4.1.3. Quando se deve usar EJB?


Essa é uma das decisões mais importantes acerca do projeto, pois envolve
muitos pontos cruciais, como tempo de desenvolvimento, experiência da equipe,
facilidade de testes, ferramentas produtivas etc.
O uso de EJB deve ser ditado se a aplicação será distribuída ou não. Se o caso
for desenvolver uma aplicação baseado em RMI/IIOP, EJB é a tecnologia J2EE que
mais é indicada para a implementação.
Com o uso de EJB com interfaces remotas, nós deixamos o servidor J2EE
tratar toda a comunicação remota da aplicação. A alternativa de se criar seu próprio
servidor RMI não é aconselhável por diversos motivos: não padronizado,
necessariamente difícil de implementar, e provadamente muito mais difícil de
manter.

4.1.3.1. Gerenciamento de transações


Se o uso de EJB não é requerido para arquiteturas distribuídas, existe ainda
um outro bom motivo para o uso de EJB. Como mostrado no capítulo 3, o
gerenciamento de transações pelo container (Container Manager Transaction) é
provavelmente uma das maiores causas de uso de EJB.
O servidor J2EE é responsável por toda a infra-estrutura para o uso e
propagação de transações. Ele coordena em um nível considerado baixo os
protocolos transacionais. O servidor também é responsável pela propagação das
transações entre os dispositivos J2EE, sendo esses locais ou distribuídos.
Uma das promessas de J2EE era deixar o baixo nível de implementação de
47
48

transações a cargo do servidor, deixando isso totalmente abstrato aos


desenvolvedores. Estes apenas usam de forma declarativa, ou programática (dentro
dos EJBs).
Os EJBs são os componentes de negócios de J2EE que são capazes de
administrar transações. Essas transações podem ser demarcadas de uma maneira
declarativa através do descritos dos EJBs, ou podem ser demarcadas dentro de
métodos do próprio EJB usando a interface UserTransaction oriunda da API JTA.
Recomenda-se o uso de demarcações declarativas, pois ficam independentes da
implementação do EJB e podem ser mudadas sem que haja uma recompilacão da
classe do EJB.

4.1.3.2. Aspectos negativos de EJB


Fora o uso de EJBs orientados a mensagens, para cada EJB é necessário o
desenvolvedor implementar pelo menos três classes Java.
• Interface home local ou remota. Essa interface permite a localização e
criação de componentes EJBs. O servidor deve prover a implementação
dessas interfaces em tempo de deploy.
• Interface de negócios local ou remota. Essa interface provê os métodos
de negócio que os clientes são capazes de acessar no EJB. O container
deve prover a implementação dessas classe em tempo de deploy de
acordo com a classe de implementação do EJB.
• Classe de implementação do EJB. Essa classe deve implementar a
interface javax.ejb.SessionBean ou javax.ejb.EntityBean, definindo, com
isso, os métodos de ciclo de vida do EJBs, além de definir a
implementação da interface de negócio.
Além desses requisitos, cada grupo de EJB é instalado no servidor de
aplicação através de um arquivo comprimido chamado ejb-jar, que deve possuir um
ou mais arquivos de configuração XML, cujo qual é considerado bastante complexo.
Em tempo de execução, o servidor de aplicação é responsável por gerar algumas
partes do componente EJB, como os stubs e esqueletos presentes em qualquer
aplicação distribuída.
Percebe-se claramente que o desenvolvimento de EJBs é bem mais complexo
que o desenvolvimento de classes Java normais chamadas de POJOS (Plain Old

48
49

Java Objects). Os clientes, para acessarem EJBs precisam fazer consultas através
de JNDI, além de tratar uma série de exceções da plataforma. Mas por que tudo
isso é necessário?
Para se entregar serviços com um gerenciamento transparente de transações,
o container precisa ter total controle das instâncias em tempo de execução. O uso
de classes geradas pelo container permite que as chamadas a métodos EJBs sejam
interceptadas pelo servidor, gerenciando com isso seus acessos, assegurando que a
escolha correta de contexto transacional seja fornecida [JOH2004].
Entretanto, os autores afirmam que o custo de se desenvolver e manter uma
aplicação EJB não é vantajoso o suficiente para muitas aplicações. Muitas vezes é
dada uma infra-estrutura enorme que não necessariamente será usada por
completo.

4.2. Acesso a dados em aplicações J2EE

O acesso a dados é vital para uma aplicação distribuída ou não. A performance


da fonte de dados irá implicar diretamente na escalabilidade e performance da
aplicação. Com isso, uma tarefa extremamente importante de arquitetos é a
separação do que pertence à camada de negócios e à camada de dados.
Como mencionado na seção de EJBs, para o acesso a dados, a plataforma
J2EE dispõe de componentes chamados beans de entidade que podem ser
gerenciados por container ou pelo desenvolvedor. Na especificação 1.1 do EJB, os
componentes BMP eram considerados mais performáticos, porém, com a nova
especificação 2.0, os componentes CMP passaram a ter uma maior performance,
além de ser extremamente mais simples de desenvolver que beans gerenciados
pelo desenvolvedor. Porém sempre é válido escolher o uso de entities beans para o
acesso a dados em aplicações?
Alguns pontos são levantados acerca do uso de EJB para a camada de
persistência:
• O uso de beans de entidade representa uma clara separação entre a
camada de acesso a dados e a camada de negócio, porém a escolha de
EJB na camada de dados afetará a comunicação feita na camada de
negócios, visto que trabalhar com EJB é bem mais burocrático que

49
50

trabalhar com simples objetos Java.


• O ciclo de vida dos beans de entidade é bem rígido. É difícil implementar
beans de granularidade grossa com EJB.
• Comparado com frameworks de mapeamento objeto-relacional, os CMPs
oferecem poucas funcionalidades.
• Comparado com o gasto de se usar EJBs de sessão (gerenciamento
transparente de transação, segurança) os beans de entidade oferecem
maior complexidade.
Um uso típico de entity beans é o uso de EJBs de sessão como fachada para o
acesso, proporcionando, com isso, um melhor meio de acesso. Porém, deve-se
perceber que os EJBs de entidade podem ser efetivos em algumas aplicações J2EE,
apesar de ela não ser a melhor escolha entre a camada de negócios e a camada
física de armazenamento.
Como veremos a seguir, algumas alternativas podem ser escolhidas para
proporcionar uma camada de acesso a dados mais simples e flexível. Pode-se
continuar usando EJBs de sessão para manipular as regras de negócios, porém
pode-se escolher um framework objeto-relacional ou simplesmente usar algumas
classes helpers em conjunto com o padrão DAO (visto no capítulo 2). Essa técnica
também é chamada de Session Managed Persistence (SMP), em contraste com o
CMP e BMP dos beans de entidade. [JOH2004].
Neste sentido, pode-se concluir que a escolha de se usar EJBs é bem custosa
e não aplicável na maioria dos casos. Apesar de muitos serviços oferecidos pelos
servidores de aplicação, como o custo de manutenção, desenvolvimento e
treinamento para um projeto, que na maioria das vezes é desenvolvido em um
tempo mínimo e com diminuição de gastos, o preço alto que se paga não vale a
pena.
Com o levantamento dos problemas acima, a elaboração de uma arquitetura
capaz de suprir os requisitos suportados pelos EJBs deve ser cuidadosamente
elaborada e explicada, conforme pode-se verificar no capítulo 5.

50
51

Capítulo 5

Uso de arquiteturas leves com a adoção de novos conceitos

A demanda por projetos com um prazo cada vez menor e com poucos
recursos, a fim de alocarem profissionais ou comprar ferramentas, vem se tornando
uma constante no campo de desenvolvimento de software no cenário brasileiro e
mundial. Fica praticamente inviável a elaboração de arquiteturas com grau de
complexidade elevado. Tendo isto em mente, os autores pretendem mostrar
detalhes da elaboração de uma arquitetura bem mais viável para o desenvolvimento
de software.
Tal arquitetura não adotará o modelo de componentes padrão do J2EE (EJBs),
com isso, a arquitetura se baseia em alguns projetos de código aberto desenvolvidos
pela própria comunidade Java. Além do uso de frameworks para a diminuição do
trabalho, a utilização de novos conceitos será fundamental para a disponibilização
de recursos que antes eram apenas possíveis dentro de um servidor de aplicação
através de EJBs. Dentre os conceitos, pode-se citar: gerenciamento declarativo de
transações; Inversão de controle; orientação a Aspectos, Mensagens orientadas a
objetos Java normais, fácil testabilidade, entre outros.

5.1. Diminuição de Acoplamento

Um aspecto presente em diversas arquiteturas J2EE é o forte acoplamento


entre as camadas, mesmo com a utilização do modelo de componentes EJBs, que
se responsabilizam pela camada de negócios e também a camada de dados
possuem grande acoplamento a certas tecnologias. Um exemplo típico é o uso de
JNDI, uma API para a busca de recursos e localização de EJBs remotos ou não.
Cada componente EJB, para obter acesso a outros componentes, como origens de
dados, filas de mensagens, ou configurações específicas, é obrigado a utilizar o
JNDI para realizar a busca pelo recurso, tratando as devidas exceções provenientes
da API.
Para um cliente acessar um EJB, é necessário realizar a mesma consulta por
JNDI, se o EJB for remoto, ele deverá também fazer uma conversão para que
recursos disponíveis do protocolo IIOP, deixando o código ainda mais acoplado à

51
52

tecnologia J2EE. Na listagem 5.1 temos um exemplo de código de acesso a


componentes EJBs por um cliente comum; nota-se que não se trata de uma tarefa
trivial.

Hashtable env = new Hashtable();


env.put("java.naming.factory.initial","org.jnp.interfaces.NamingContext-
Factory");
env.put("java.naming.factory.url.pkgs","org.jboss.naming:org.jnp.inter-
faces");
env.put("java.naming.provider.url","localhost");
InitialContext context = new InitialContext(env);
Context ctx = new InitialContext(env);
Object obj=ctx.lookup("java:comp/env/Hello");
HelloHome home HelloHome)PortableRemoteObject.narrow(obj,HelloHome.class);
Hello hello=home.create();
System.out.println(hello.hello());

Listagem 5.1: Código de acesso a componentes EJB

Com um código muito acoplado a certas tecnologias, a manutenibilidade e uma


eventual mudança podem se tornar tarefas bastante custosas.
Os autores entendem que se deve procurar diminuir o acoplamento a camadas
de um sistema. Com a adoção de frameworks para o gerenciamento de
componentes de uma forma limpa, pode-se minimizar o esforço de manutenção do
código. No presente trabalho, os autores dão ênfase ao framework Spring
[SPR2006] para a administração dos recursos, além do ciclo de vida dos
componentes.
O Spring trabalha com a idéia de classes simples Java fornecerem
funcionalidades para qualquer sistema. É um framework orientado a POJOS. Para o
gerenciamento de beans, ele possui um fábrica de beans que pode disponibilizar
varias instâncias ou uma única instância de um bean para a aplicação (Singleton).
Os componentes são registrados e configurados em um arquivo XML,
minimizando, assim, a necessidade de recompilacão quando uma eventual mudança
de configuração for necessária.
Usando conceitos como inversão de controle, o Spring diminui o acoplamento
entre módulos e entre componentes. Os detalhes sobre Inversão de controle serão
explicados na próxima seção.

52
53

5.2. Inversão de Controle

O conceito de inversão de controle não é atual, ele surgiu com o princípio da


inversão de dependência (Dependency Inversion Principle – DIP), que é uma
estrutura resultante do uso de dois princípios: Open-Closed Principle (OCP) e Liskov
Substitution Principle (LSP). Este princípio prega que “os módulos de alto nível não
devem depender dos níveis mais baixos. Ambos devem depender de abstrações”, e
também prega que “As abstrações não devem depender de detalhes. Estes devem
depender das abstrações” [MAR1996].
Ou seja, o padrão inversão de controle (Inversion of Control – IoC) é um padrão
que “mostra” como o desenvolvedor deve projetar suas classes e suas
interdependências de uma maneira que tenha um baixo acoplamento, ficando o
máximo possível independente uma classe da outra.
De acordo com [WIK2006], Inversão de controle é “o nome dado ao padrão de
desenvolvimento de software onde o controle de chamadas de métodos não é
determinado pelo programador. Este controle é delegado a um container”.

5.2.1. Formas de implementação de Inversão de Controle


De acordo com [FOW2004], o padrão de Inversão de Controle pode ser
implementado através de três formas: Setter Injection, Constructor Injection e
Interface Injection.

5.2.1.1. Setter Injection


Esta é uma forma de implementação de Inversão de Controle que utiliza os
métodos “setter” para injetar o objeto referenciado no objeto corrente. Segundo
[MAL2005], esta forma é muito útil quando se tem um objeto que necessita modificar
suas propriedades várias vezes durante o seu ciclo de vida. A principal desvantagem
do Setter Injection é que ele burla os princípios da orientação a objetos: o
encapsulamento e o ocultamento de informações.
Em [FOW2004], temos um exemplo bem simples desta forma de Inversão de
Controle, utilizando a plataforma Java e o framework Spring. Este exemplo é
mostrado a seguir:
A classe “MovieLister” é um JavaBean simples, que possui uma referência da
interface “MovieFinder”, e implementa o método setter desta referência, A interface,
por sua vez, possui apenas o método “findAll”:

53
54

public interface MovieFinder {


List findAll();
}

Listagem 5.2: Exemplo de Interface com método findAll

public class MovieLister {


private MovieFinder finder;
public void setFinder(MovieFinder finder) {
this.finder = finder;
}
}

Listagem 5.3.: Exemplo de classe que implementa o método setter

Há também uma classe denominada “ColonMovieFinder”, que possui apenas


um atributo do tipo String onde irá posteriormente ser injetado um arquivo (neste
exemplo, um arquivo texto).

public class ColonMovieFinder {


private String filename;
public void setFilename(String filename) {
this.filename = filename;
}
}

Listagem 5.4: Exemplo de classe que possui um atributo posteriormente injetado

Por fim, há um arquivo XML que possui toda a configuração, onde será ditada a
injeção. A configuração também poderia ser feita através de código.

<beans>
<bean id="MovieLister" class="MovieLister">
<property name="finder">
<ref local="MovieFinder"/>
</property>
</bean>
<bean id="MovieFinder" class="ColonMovieFinder">

54
55

<property name="filename">
<value>movies1.txt</value>
</property>
</bean>
</beans>

Listagem 5.5: Arquivo XML com configuração da injeção

5.2.1.2. Constructor Injection


Esta é uma forma de Inversão de Controle que utiliza o construtor da classe
para injetar a referência ao objeto. A principal vantagem do Constructor Injection é
que apenas o criador da classe conhece as propriedades do objeto referenciado,
diferentemente do Setter Injection, onde as propriedades do objeto referenciado são
públicas.
Seguindo o exemplo do Setter Injection, [FOW2004] também mostra um
exemplo simples do uso do Constructor Injection, com a diferença de que, ao invés
de usar o framework Spring, ele utiliza o framework PicoContainer:
A classe “MovieLister” será modificada, onde, ao invés de ter um atributo do
tipo “MovieFinder” (definido anteriormente), terá um construtor que recebe como
parâmetro este atributo:

public class MovieLister {


public MovieLister(MovieFinder finder) {
this.finder = finder;
}
}

Listagem 5.6: Classe onde será feita a injeção através do construtor

Da mesma forma, a classe “ColonMovieFinder” também deixa de ter um


atributo do tipo String, e passar a receber no seu construtor um parâmetro deste
atributo:
public class ColonMovieFinder {
public ColonMovieFinder(String filename) {
this.filename = filename;
}
}

Listagem 5.7: Classe a lógica fica localizada no seu construtor

55
56

O arquivo de configuração, nesse exemplo, é um método Java, que recebe o


arquivo texto como um parâmetro constante e registra as classes onde serão
injetados os objetos.

private MutablePicoContainer configureContainer() {


MutablePicoContainer pico = new DefaultPicoContainer();
Parameter[] finderParams = {new
ConstantParameter("movies1.txt")};
pico.registerComponentImplementation(MovieFinder.class,
ColonMovieFinder.class, finderParams);
pico.registerComponentImplementation(MovieLister.class);
return pico;
}

Listagem 5.8: Arquivo Java de configuração de injeção

5.2.1.3. Interface Injection


Nesta forma de Inversão de Controle é criada uma Interface para o framework
IoC (Inversion of Control). É nesta interface que os objetos irão ser injetados. De
acordo com [MAL2005], a principal desvantagem desta forma de inversão de
controle é que a criação da interface faz com que a aplicação fique presa a
determinado framework.
Da mesma maneira que as duas formas anteriores, [FOW2004] mostra um
exemplo de utilização da Interface Injection, utilizando o framework Avalon:
Primeiramente é criada uma interface que irá injetar os objetos. Esta interface
terá um método que recebe como parâmetro um atributo do tipo “MovieFinder”
(definido anteriormente):

public interface InjectFinder {


void injectFinder(MovieFinder finder);
}

Listagem 5.9: Interface que irá injetar objetos através do Interface Injection

56
57

A classe “MovieLister” é modificada novamente. Desta vez, esta classe irá


implementar a interface definida acima, e, de acordo com a regra, a classe terá que
implementar o(s) método(s) definidos na interface:

public class MovieLister implements InjectFinder {


public void injectFinder(MovieFinder finder) {
this.finder = finder;
}
}

Listagem 5.10: Classe que implementa a interface do interface injection

Da mesma forma para a classe “ColonMovieFinder”, que irá implementar uma


interface “InjectFinderFilename”, e, conseqüentemente, o método definido na
interface.

public interface InjectFinderFilename {


void injectFilename (String filename);
}

Listagem 5.11: Interface que deverá ser implementada

public class ColonMovieFinder implements MovieFinder,


InjectFinderFilename {
public void injectFilename(String filename) {
this.filename = filename;
}
}

Listagem 5.12: Classe que implementa a interface da listagem 5.11

Por fim, é criada uma classe de configuração. Esta classe irá registrar as
classes que possuem os objetos a serem injetados, igualmente como ocorre com o
Constructor Injection. Além disso, também há um método que registra as interfaces
definidas:

57
58

public class Config {


private Container container;

private void configureContainer() {


container = new Container();
registerComponents();
registerInjectors();
container.start();
}

private void registerComponents() {


container.registerComponent("MovieLister",
MovieLister.class);
container.registerComponent("MovieFinder",
ColonMovieFinder.class);
}

private void registerInjectors() {


container.registerInjector(
InjectFinder.class,
container.lookup("MovieFinder"));
container.registerInjector(
InjectFinderFilename.class, new
FinderFilenameInjector());
}
}

Listagem 5.13: Arquivo de configuração para o Interface Injection

Porém, [FOW2004] trata a Inversão de Controle como um sinônimo de


dependency Injection, já [HAR2005] mostra que são conceitos distintos.
Para [HAR2005], Dependency Injection é um subtipo de Inversão de Controle,
ou seja, sempre que falar de Dependency Injection estará falando de Inversão de
Controle, porém, nem sempre que falar de Inversão de Controle estará falando de
Dependency Injection.
Neste sentido, [HAR2005] cita que a Inversão de Controle é dividido em duas
partes: Dependency Injection e Dependency Lookup.

58
59

O Dependency Lookup é um conceito mais antigo, enquanto que o


Dependency Injection é mais atual, e também mais flexível e usado que o outro.
Outra diferença entre eles é que com o Dependency Lookup o componente deve
obrigatoriamente adquirir uma referência da dependência, e no Dependency
Injection a dependência é injetada no componente através do container IoC.
Cada subtipo de Inversão de Controle possui subtipos também. O Dependency
Lookup é dividido em Dependency Pull e Contextualized Dependency Lookup (CDL),
ambos conceituados a seguir. E o Dependency Injection é dividido em Setter
Injection e Constructor Injection, ambos conceituados anteriormente.
• Dependency Pull: Este tipo de Dependency Lookup é comum para os
desenvolvedores Java, pois eles normalmente utilizam-no para acessar um EJB,,
por exemplo. No Dependency Pull, a dependência é conseguida através de
registros.
• Contextualized Dependency Lookup: Este tipo é bastante similar ao anterior,
porém, ao invés de conseguir a dependência através de um registro, o lookup é
executado de acordo com o container que gerencia o recurso.

5.2.2. Constructor Injection x Setter Injection


Apesar de haver vários tipos e subtipos de Inversão de Controle, os mais
utilizados são o Constructor Injection e o Setter Injection.
[FOW2004] faz uma comparação interessante entre as duas formas de
Inversão de Controle.
Uma das vantagens do Constructor Injection foi mostrada por [BEC1996], que
diz que “os construtores com parâmetros mostram um enunciado claro do que
significa criar um objeto válido em um lugar óbvio”. Porém, pode haver casos em
que seja preciso a existência de muitos construtores, o que torna o código muito
confuso.
Por este motivo há os métodos-Fábrica, que podem combinar construtores
privados com métodos setter. Entretanto também há problemas com a utilização
desses métodos: normalmente eles são métodos estáticos, e, portanto, não podem
ser usados em interfaces [FOW2004].
Em uma entrevista dada para [SER2006], Aslak Hellesoy, desenvolvedor
Sênior da empresa ThoughtWorks Inc., diz que “... não há diferenças muito grandes
entre o Constructor Injection e o Setter Injection”. Para ele, o Constructor Injection é
59
60

melhor, pois, primeiramente, se houver objetos imutáveis na classe, com o Setter


Injection, terá que ter várias flags indicando se o objeto é nulo ou não, com o intuito
de evitar que o usuário invoque o método setter inadequadamente.
Outro motivo dado por Hellesoy é também conhecido como “bom cidadão”
(termo dado por Josh Bloch), que diz que quando uma classe é instanciada, já está
pronta para ser usada. Com o Constructor Injection, o usuário só pode criar os
componentes que irão utilizar esta forma de Inversão de Controle depois de passar
tudo o que ela irá precisar. Já com o Setter Injection, o usuário pode criar o
componente que vai utilizar esta forma de Inversão de Controle, mas também tem
que lembrar de invocar os métodos setter.
Os autores deste trabalho e [HAR2005] preferem usar o Setter Injection, devido
ser, nas suas opiniões, mais simples de utilizar e, principalmente, pelo motivo
mostrado anteriormente, onde pode haver vários construtores, tornando o código
confuso.

5.3. Testabilidade em Aplicações J2EE

O uso de testes em qualquer aplicação, seja ela Web ou Desktop, é muito


importante, pois os testes ajudam a aplicação a conter menos erros, aumentando a
qualidade da aplicação. Além disso, pode-se citar como as maiores motivações para
testar uma aplicação: Validação de código, identificação de problemas, bugs
drásticos e verificação de comportamentos sobre condições específicas.
Neste sentido, pode-se dizer que uma aplicação tem alta testabilidade se ela
tende a expor suas falhas durante os testes com entradas que geram defeitos. Uma
aplicação tem baixa testabilidade se ela tende a ocultar as falhas detectadas durante
os testes, produzindo saídas corretas para entradas que geram defeitos. Outro fator
que contribui para que uma aplicação tenha uma baixa testabilidade é quando os
requisitos desta são incompletos ou desatualizados.
Com a crescente complexidade dos softwares, torna-se fundamental a
utilização de ferramentas de teste visando a automatização dessa atividade. O teste
de software é, em geral, custoso e propenso a erros e ferramentas que apóiem essa
atividade são essenciais. Atualmente, encontram-se disponíveis diversas
ferramentas que buscam atender a esse propósito, no entanto, a maioria oferece
suporte apenas ao teste estrutural (neste tipo de teste, também conhecido com

60
61

caixa-branca, é verificado se a estrutura interna da unidade está correta e se todos


os caminhos internos possíveis do objeto devem ou não ser percorridos). Além
disso, as poucas ferramentas que implementam a técnica funcional (essa técnica,
também conhecida como caixa-preta, possui os testes que encaram o sistema a ser
testado como uma função que mapeia um conjunto de valores de entrada em um
conjunto de valores de saída sem se preocupar com a forma como esse
mapeamento foi implementado, diferentemente do teste estrutural) desconsideram
seus principais critérios e não permitem análise de cobertura apropriada.
É importante ressaltar que o objetivo dos testes é encontrar erros. Se um teste
não encontra nenhum problema onde está testando, provavelmente o teste está
errado. Além disso, é extremamente importante testar uma aplicação
frequentemente e desde o início do processo da mesma, pois os desenvolvedores
tendem a cometer erros mais quando estão recolhendo requisitos do que durante o
processo de desenvolvimento propriamente dito, além do custo dos erros crescer
exponencialmente com o atraso na descoberta dos mesmos, devido o trabalho de
desenvolvimento de um software se basear em trabalhos anteriores.

5.3.1. Técnica Funcional


Como falado anteriormente, a técnica funcional (caixa-preta) tem como objetivo
encontrar diferenças entre o comportamento que o sistema está desenvolvendo e o
comportamento proposto na sua especificação. Isto é, nestes testes são
consideradas apenas as entradas e as saídas, fazendo com que o testador sequer
tenha acesso ao código-fonte.
Além disso, nos testes funcionais as aplicações são testadas com o objetivo de
se encontrar cinco “tipos” de erros: funções incorretas ou omitidas, erros de
interface, erros de estrutura de dados ou de acesso a dados externos, erros de
comportamento e erros de iniciação e término.
A figura 5.1 mostra como funcionam os testes funcionais (caixa preta):

61
62

Figura 5.2: Funcionamento dos testes funcionais

A maioria das ferramentas que auxiliam os testes oferece apoio apenas para o
teste estrutural (mostrado em 5.3.2), porém algumas ferramentas específicas para
testes funcionais encontram-se disponíveis, como TestComplete [TES2006],
SPACES [DAN2006], Jtest [JTE2006], XDE tester [XDE2006] e o framework JUnit
[JUN2006], este, apesar de não apoiar nenhuma técnica de teste específica, pode
ser utilizado para especificação e execução dos casos de teste [DAN2006].
A tabela 5.1, mostra um comparativo entre as ferramentas acima citadas,
enfatizando suas características principais com relação aos testes funcionais.

Tabela 5.1.: Comparativo entre ferramentas de teste funcional [DAN2006]

Como mostra a tabela, duas características não têm suporte (Especificação de


classes de equivalência e Especificação de valores-limite), ambas são
características específicas dos testes funcionais. Na especificação de classes de
equivalência, o domínio de entrada de dados é dividido em classes de dados. A
divisão é feita em classes de equivalência válidas e inválidas. Já a especificação de
valores-limite é uma complementação da anterior, através de casos de teste que
exercitam os valores limítrofes. Além disso, a tabela mostra claramente que as
ferramentas JTest e SPACES são as mais completas.

62
63

5.3.2. Técnica Estrutural


Conforme dito anteriormente, a técnica estrutural (ou caixa-branca), ao
contrário da técnica funcional, tem conhecimento do funcionamento interno das
classes que irão ser testadas, não importando apenas as entradas e saídas, e sim
todas as partes do código do sistema.
Há basicamente dois tipos de testes que fazem parte da técnica estrutural: os
testes unitários (que testam cada componente por vez) e os testes de integração
(que testam um conjunto de componentes conjuntamente).
A figura 5.2. mostra como funciona os testes estruturais (caixa branca) do tipo
unitários:

Figura 5.3: Funcionamento de testes unitários

5.3.3. Testes na metodologia Extreme Programing (XP)


Extreme Programing é uma metodologia ágil para equipes pequenas e médias
desenvolvendo software com requisitos vagos e em constante mudança (Kent Beck,
criador da metodologia XP). Ou seja, é um desenvolvimento rápido e consistente
com as reais necessidades do cliente e fácil manutenibilidade, permitindo que o
software seja modificado à medida que as necessidades do negócio se alteram ou
ampliam.
Esta metodologia é baseada em quatro princípios: Simplicidade, Feedback,
Coragem e Comunicação. Os testes se encaixam em três dos quatro princípios.
Na simplicidade, os testes, assim como os modelos e os códigos, são
simplificados. No feedback, há a necessidade de testes unitários, tanto para ter o
feedback para os desenvolvedores, como para o desenvolvedor poder gerar
releases (versões) do sistema em um curto espaço de tempo sem os chamados

63
64

bugs. Finalmente, na comunicação, são feitos os testes unitários propriamente ditos,


que, baseados em códigos simples (como dito anteriormente na simplicidade), serão
simplificados e precisos, além de oferecerem o estado e o comportamento da
aplicação, e, principalmente, devem ser realizados antes da fase de codificação.
A XP possui doze requisitos básicos, e o teste antes da codificação é um
destes requisitos. Os testes em XP são divididos em duas categorias: testes
unitários e testes de aceitação. Testes unitários são, em geral, escritos pelos
desenvolvedores e têm a finalidade de testar uma classe individual ou um pequeno
grupo de classes. Já os testes de aceitação são usualmente escritos pelos próprios
clientes ou por uma equipe de teste externa (com a ajuda dos desenvolvedores) e
têm a finalidade de testar todo o sistema, de ponta-a-ponta. As atividades de teste
são realizadas durante todo o processo de desenvolvimento, e o código é construído
com o propósito de satisfazer os resultados esperados. E à medida que um novo
código é adicionado, novos testes devem ser realizados para assegurar que não
ocorram impactos negativos [JOH2004].

5.3.3.1. Testes Unitários no XP


Os testes unitários são um dos elementos chave em XP [WEL2005], pois são
criados antes do código e armazenados em um repositório junto ao código que será
testado. Um código que não possua seu respectivo teste unitário não deve ser
liberado, pois cada parte do sistema com possibilidade de falha precisa ser
verificada. Além disso, a criação de testes unitários antes do código provê uma
melhor compreensão dos requisitos e direciona o foco dos desenvolvedores.
Um fator importante para a utilização de testes unitários, especialmente se
estes forem automatizados, é a economia de custo que podem proporcionar ao
identificar e oferecer proteção contra erros. É relevante considerar que descobrir
todos os problemas que podem ocorrer durante o desenvolvimento consome uma
grande quantidade de tempo, tornando-se necessário que um conjunto completo de
testes unitários esteja disponível logo no início, e não no último mês do projeto.
Nesta etapa os desenvolvedores criam e executam testes unitários toda vez
que um código é escrito ou modificado. Consertar pequenos problemas assim que
são encontrados, em geral, leva menos tempo do que consertar grandes problemas
a poucas horas do prazo final. Outro benefício dos testes unitários automatizados é
a possibilidade de combinar um conjunto de alterações com a última versão liberada

64
65

e liberar novas versões em um curto espaço de tempo.

5.3.3.2 Testes de Aceitação


Os testes de aceitação constituem uma das principais diferenças entre o XP e os
processos de desenvolvimento tradicionais. Em geral, são escritos pelos clientes ou
usuários finais através das user stories [WEL2005], com a assistência de um
indivíduo da equipe responsável por testar o software. Durante uma iteração, as user
stories selecionadas durante a reunião de planejamento de iteração serão traduzidas
em testes de aceitação. Uma user story pode ter um ou mais testes de aceitação
para assegurar que a sua funcionalidade esteja de acordo com a especificação.
Após a implementação da user story, o cliente especifica cenários para serem
testados, e ela não é considerada completa até que tenha passado por esses testes
de aceitação. Isso significa que novos testes de aceitação devem ser criados a cada
iteração, ou a equipe de desenvolvimento não deverá reportar progresso.
Testes de aceitação são testes de sistema de caixa preta e cada um deles
representa algum resultado esperado. Testes de aceitação também são usados
como testes de regressão anteriores à liberação de uma versão do software. Nesse
contexto, a Garantia de Qualidade (QA) é uma parte essencial do processo XP. Em
alguns projetos QA é realizada por um grupo especializado, enquanto em outros QA
é integrada à própria equipe de desenvolvimento. Em ambos os casos XP requerem
que o desenvolvimento tenha uma relação muito próxima com QA.
Dentre as principais metas dos testes de aceitação estão: fornecer aos clientes,
gerentes e desenvolvedores a confiança de que o produto inteiro está progredindo
na direção certa; e checar cada incremento no ciclo XP para verificar se o valor de
negócio está presente [CRI2000]. Os testes de aceitação, que são responsabilidade
do testador e do cliente, são testes ponta-a-ponta sob a perspectiva do cliente, e não
testes que verificam cada caminho possível no código (essa é a finalidade dos testes
unitários).

5.3.4. Testes com EJB


Como mostrado no capítulo 3, o Enterprise JavaBean (EJB) possui serviços de
container. Devido o EJB depender destes serviços, testar um EJB é um trabalho
árduo, difícil e bem mais complexo do que testar classes Java comuns.
Com o EJB não se pode simplesmente instanciá-lo e testá-lo como se faz com
classes Java, pois os EJB são objetos gerenciáveis, gerenciados pelo container.
65
66

Com isso, há três maneiras de se testar um EJB [JOH2004]:


• Desenvolver um teste que serve como um cliente remoto de um container
EJB;
• Desenvolver e deploy um teste que será executado em um servidor de
aplicação. Esta é uma boa estratégia para testar EJB em uma interface
local;
• Testar com objetos stubs modificando objetos do container. Este tipo de
teste geralmente funciona apenas quando os EJBs possuem requisitos
simples do container EJB.
Dentre estas três alternativas, a mais simples (ou menos complexa) é a
primeira. [JOH2004] descreve como deve ser feito o teste nesta alternativa:

“Primeiramente, escreve-se os casos de teste, os quais conectam com o servidor EJB e são
executados em uma JVM (Java Virtual Machine) diferente da JVM utilizada pelo container EJB. Pode-
se invocar os casos de teste normalmente, como qualquer outro teste, com a diferença de tomar
cuidado em prover as propriedades JNDI apropriadas que permitem a conexão do container EJB”.

A tabela 5.2 mostra as vantagens e desvantagens da utilização de cada uma


destas três alternativas:

Alternativa Vantagens Desvantagens


• Fácil de escrever e • Não é possível
executar testes testar interfaces
locais
• Pode usar a
estrutura padrão do
JUnit
Teste como um
cliente remoto de um • As interfaces
container EJB remotas expostas pelo
EJB em uma aplicação
distribuída normalmente
expõe uma lógica de
negócio da aplicação,
tornando um local

66
67

apropriado para testes

• Em aplicações web, • Requer um


provavelmente os framework de teste
testes terão o mesmo adicional ao JUnit
Teste executado em acesso a um EJB como
• Os testes
um servidor de aplicação as aplicações que usam
possuem uma
o EJB propriamente
implementação mais
dito.
complexa.

• É possível executar • Devido não


os testes sem um necessitar de um
container EJB container, deve-se
Testes com objetos
escrever muito
stubs modificando objetos • É possível reusar
do container código que simula o
estruturas de
container
componentes em várias
aplicações

Tabela 5.2.: Tabela comparativa das três alternativas de testes com EJB

Outra possibilidade de testar um EJB é simular um container para prover os


serviços necessários para um EJB funcionar. Entretanto, esta é uma possibilidade
impraticável, devido à arquitetura EJB ser altamente complexa. Alem disso, a
segurança e o gerenciamento de serviços são difíceis de simular.
Esta dificuldade de testar um EJB é um dos principais motivos que o tornam
uma má alternativa de desenvolvimento, além de o EJB ser bastante complexo e
burocrático por si só.

5.4. Camada de acesso a dados simplificada


O acesso a dados de qualquer aplicação Web pode se tornar complexo,
dependendo de como for implementada. Para facilitar este acesso, vários
frameworks foram surgindo, sendo que alguns acabam por diminuir a performance
da aplicação, principalmente devido ao uso indevido por parte dos desenvolvedores.
Dois dos principais frameworks de acesso a dados existentes são o Hibernate
[HIB2006] e o IBatis [IBA2006], ambos serão discutidos em detalhes nas próximas

67
68

seções. Outro framework, novo no mercado, é o Pérola [PER2006], porém não


entraremos em maiores detalhes sobre ele. Todos os frameworks citados são
responsáveis por realizar o mapeamento objeto-relacional (responsável por fazer a
“transição” de uma aplicação orientada a objetos ou orientada a aspectos para um
banco de dados relacional, onde se aplicam à maioria dos bancos de dados
existentes atualmente, como o MySQL, Oracle, etc.) de uma aplicação.
Além dos frameworks, há também Application Program Interface (APIs) que
auxiliam no acesso a dados. Como este trabalho é desenvolvido sobre a plataforma
Java, citaremos a Java Database Connectivity (JDBC), uma API que faz o envio de
instruções SQL para qualquer Banco de Dados relacional.
Tanto o Hibernate, o IBatis como o JDBC funcionam com o Spring. De acordo
com [HAR2005], apesar dos frameworks auxiliarem bastante os desenvolvedores
nos quesitos do mapeamento objeto-relacional, e no próprio desenvolvimento, o
JDBC se torna melhor para acessar os dados, devido ele proporcionar um controle
completo da seleção dos dados. Por exemplo, com IBatis as inner classes não são
utilizadas, e todas as características padrões do banco de dados se tornam
facilmente acessíveis, pois todo o script SQL é feito manualmente.

5.4.1. Utilizando Hibernate com Spring


Uma das maiores desvantagens do JDBC é que o desenvolvimento com ele
exige uma grande quantidade de código, tornando o entendimento da aplicação
mais complexo.
Por isso, ocorreu o surgimento de frameworks, como o Hibernate, que auxilia no
mapeamento objeto-relacional. O hibernate trabalhando juntamente com o Spring,
torna a aplicação menos complexa e com menor quantidade de código-fonte. Alem
disso, a junção dos dois tem uma grande vantagem que é a não preocupação por
parte dos desenvolvedores em gerenciar a abertura e o fechamento de Sessions,
interface responsável por realizar a conexão com o banco de dados.
Para poder utilizar os dois frameworks em conjunto, é necessário efetuar
algumas configurações:
Primeiramente, deve-se criar um arquivo XML onde se configura o datasource,
responsável por definir a conexão com o banco de dados. Um exemplo é mostrado
na Listagem 5.14.

68
69

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"

"http://www.springframework.org/dtd/spring-beans.dtd">

<beans>

<bean id="dataSource"

class="org.springframework.jdbc.datasource.

DriverManagerDataSource">

<property name="driverClassName">

<value>org.hsqldb.jdbcDriver</value>

</property>

<property name="url">

<value>jdbc:hsqldb:db/app</value>

</property>

<property name="username"><value>sa</value></property>

<property name="password"><value></value></property>

</bean>
</beans>

Listagem 5.14: Configuração de DataSource para uso de Spring com hibernate

Com o Hibernate, é possível fazer todos os procedimentos de um banco de


dados, como transações, normalizações (1-1, 1-N, N-N), concorrência e lock.
Além disso, o Hibernate possui uma linguagem própria, chamada Hibernate
Query Language (HQL), utilizada para se fazer as consultas no banco de dados.
Pode-se usar tanto o HQL como o próprio SQL (Structured Query Language) para se
fazer as consultas no banco de dados, mas normalmente é aconselhado que se use
o HQL.
Uma das principais desvantagens do uso do Hibernate é que ele diminui a
performance da aplicação, além de não ser tão simples manipular os dados que
estão sendo buscados numa consulta. Já a grande vantagem do uso do mesmo é
que o tempo de desenvolvimento se torna menor com o mesmo, assim como a
manutenção do código-fonte da aplicação que o utiliza.

69
70

5.4.2. Utilizando IBatis com Spring


O IBatis, assim como o Hibernate, é um framework que executa o mapeamento
objeto-relacional de acesso ao banco de dados.
Com o IBatis, o desenvolvedor pode escrever sintaxes SQL normalmente, o
que torna o IBatis bastante interessante, já que com isso a performance da
aplicação fica bem melhor. Assim como no Hibernate, no IBatis a configuração é
feita através de um arquivo XML, conforme exemplo na Listagem 5.15. Neste
mesmo arquivo também é declarado o DataSource, da mesma forma que no
Hibernate.

<bean id = “sqlMapClient” class = “...SqlMapClientFactoryBean”>


<property name = “configLocation”>
<value>sqlMapConfig.xml</value>
</property>
</bean>

Listagem 5.15: Trecho do arquivo de configuração do Spring com IBatis (struts-config.xml)

Além disso, também é necessário um arquivo XML que declara os arquivos de


mapeamento, conforme exemplo na Listagem 5.16.

<sqlMapConfig>
<sqlMap resource=”Client.xml”/>
<sqlMap resource=”Boss.xml”/>
<sqlMapConfig>

Listagem 5.16: Trecho do arquivo de declaração dos mapeamentos (sqlMapConfig.xml)

E, finalmente, é necessário um arquivo XML para cada mapeamento


(normalmente para cada Bean que será mapeado), com o SQL propriamente dito,
conforme exemplo na Listagem 5.17.

70
71

<sqlMap>
<typeAlias alias="client" type="br.com.sistema.Client"/>
<insert id=" gravar" parameterClass="client">
INSERT INTO CLIENT (ID, NOME)
VALUES
(#id#, #nome”)
</insert>
<select id="listarPorPK" resultClass="client"
parameterClass="int">
SELECT ID as id
,NOME as nome
FROM CLIENT
WHERE ID = #id#
</select>

<delete id="excluir" parameterClass="int">


DELETE FROM CLIENT
WHERE ID = #id#
</delete>
</sqlMap>

Listagem 5.17: Exemplo de arquivo de mapeamento de um determinado Bean (Client.xml)

A principal vantagem do IBatis é que ele não gera nenhum código, além, de
não ser necessário o desenvolvedor aprender nenhuma linguagem (como o HQL, no
Hibernate), já que o IBatis utiliza o próprio SQL.
Na aplicação desenvolvida neste trabalho, foi utilizado o IBatis em conjunto
com o Spring.

5.5. Programação Orientada a Aspectos

A Programação Orientada a Aspectos (AOP) ajuda os desenvolvedores a


modificar dinamicamente seu modelo estático para incluir um código requerido para
cumprir os requerimentos secundários sem ter que modificar o modelo estático
original.
Além disso, ela permite que as classes possuam apenas o código fonte
necessário para si, que cumpram com os seus objetivos, sem a necessidade de
possuir blocos “auxiliares” para outros objetivos, como auditoria, segurança, etc.
Pode-se dizer que a AOP complementa a programação Orientada a Objetos com a
grande vantagem da redução da complexidade do projeto, aumentando sua
modularização, através da clara separação dos chamados requisitos funcionais

71
72

(denominados Objetos) e não-funcionais ou entrelaçados (denominados Aspectos).


Com a separação destes blocos, podem-se obter algumas vantagens, como:
• Códigos mais simples, uma vez que as classes possuem apenas as
regras de negócio, deixando de lado aspectos de projeto;
• Alterações nos aspectos de projeto não geram impacto nas classes.
As classes passam a possuir apenas a regra de negócio;
• Maior produtividade na manutenção, uma vez que um mesmo aspecto
pode ser aplicado às diferentes classes de um projeto.
• Útil para implementar distribuição, controle de concorrência, e
persistência;
• Modularidade, reutilização, e extensibilidade de software;
• Apóia o desenvolvimento com IDEs (Integrated Development
Environments);
• Produtividade;
• Separação na implementação e testes.

Não há ainda nenhuma linguagem que possua suporte nativo à Orientação a


Aspectos. Por este motivo, surgiram vários frameworks para auxiliar na mesma,
como AspectJ [ASP2006], AspectWerkz [ASP2005], JBossAOP [JBO2005] e o
Spring Framework [SPR2006].
Neste trabalho nos aprofundaremos apenas no Spring Framework, conforme
descrito na próxima seção.

5.5.1. Programação Orientada a Aspectos com Spring


O trabalho não visa um aprofundamento nos conceitos de aspectos, mas sim
tem a preocupação de mostrar como usar aspectos para solucionar requisitos do
cotidiano de uma aplicação e, sobretudo implementar funcionalidades antes
presentes apenas em servidores de aplicação, usando o modelo de componentes
EJB, como a presença de demarcação declarativa de transações.
O framework Spring fornece um módulo de seu projeto todo voltado a
programação orientada a aspectos, com uma série de serviços que fazem o uso de
AOP bem mais simples. Além disso, outros componentes no Spring, como
gerenciamento declarativo de transações e classes auxiliares para EJB, provém um
serviço baseado em AOP para a simplificação de tarefas em sua arquitetura.

72
73

5.6. Gerenciamento robusto e elegante de Transações

Como mencionado anteriormente, o foco na seção de AOP será para mostrar


como funciona o gerenciamento declarativo de transações que o framework Spring
proporciona.
Se o desenvolvedor optar por usar Spring ou não, uma importante decisão
deve ser tomada em uma arquitetura quando se usa transações: Devem-se usar
transações globais ou locais?
Transações locais são especificas a um simples recurso transacional (uma
conexão JDBC, por exemplo), já uma transação global é gerenciada por um
container e pode ter diversos recursos transacionais. [HAR2005]
Na maioria das ocasiões, sistemas pequenos e simples, uma escolha de
transações locais é normal, acarretando, com isso, uma grande quantidade de
código de gerenciamento de transação em diversos pontos do código, além de que
se no futuro for necessário usar um esquema global de transação, os pontos de
transações locais terão que ser abandonadas.
O uso de transações globais sem o uso do Spring normalmente é feito através
da API JTA12, que é considerada bem complexa e depende da API JNDI,
significando que é necessário o uso de um servidor de aplicação. Assim, com JTA é
possível utilizar EJB e sua funcionalidade de gerenciamento declarativo de
transações, proveniente de um servidor de aplicação.
Com a utilização do framework Spring, pode-se tirar proveito do suporte a
transações globais de forma declarativa. Declarativo nesse contexto significa que se
declara para o Spring se um método possui um escopo transacional, com isso o
framework assegura que exista uma transação apropriada para o contexto quando o
método for chamado. Utilizando fortemente interceptores nas chamadas de métodos
por AOP [HAR2005].
A grande vantagem de se usar a forma declarativa de demarcação de
transações é que não é necessário modificar o código para se alterar a definição
transacional.
Na listagem 5.18, há um exemplo de configuração de demarcação transacional
para os métodos do serviço.

12
API para tratamento de transações na plataforma Java.

73
74

<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"


"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="transactionManager"

class="org.springframework.transaction.jta.JtaTransactionManager" />

<bean id="uc" abstract="true"

class="org.springframework.transaction.interceptor.TransactionProxyFactoryB
ean">
<property name="transactionManager">
<ref bean="transactionManager" />
</property>
<property name="transactionAttributes">
<props>
<prop key="*">PROPAGATION_REQUIRED,ISOLATION_DEFAULT,-
BMSBusinessException</prop>
</props>
</property>
</bean>

<!-- Datasource da aplicação -->


<bean id="dataSource"
class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="java:comp/env/jdbc/mes" />
</bean>

<bean id="sqlMapClient"

class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
<property name="configLocation">

<value>classpath:br/com/bms/mes/aci/eis/data/daoimpl/ibatis/sql-map-
config.xml</value>
</property>
<property name="dataSource">
<ref bean="dataSource" />
</property>
</bean>

74
75

<bean id="dao" abstract="true">


<property name="sqlMapClient">
<ref bean="sqlMapClient" />
</property>
</bean>

</beans>

Listagem 5.18: Arquivo de configuração de demarcação transacional para os métodos do


serviço

No trecho acima percebemos alguns beans sendo configurados. O primeiro


passo é a declaração de nosso gerenciador de transações (transactionManager),
posteriormente, nesse exemplo, criamos uma classe abstrata uc referente a todas as
nossas classes de negócio.
No bean uc, é usado um proxy para que quando alguém tente chamar alguma
vez uma classe de negócio, o proxy entre em ação e intervenha atribuindo ou não o
contexto transacional no contexto.
Por fim, percebemos a propriedade injetada por IoC transactionAttributes, que
define quais métodos serão beneficiados pelo contexto transacional, que tipo de
contexto transacional será usado (usar uma transação existente, criar sempre uma
nova transação, entre outras), o tipo de isolamento das transações e por último as
exceções que automaticamente darão rollback na transação se for lançada no
método configurado.
Com isso, os autores acreditam que se pode ter um ambiente com contexto
transacional declarativo sem a necessidade de um servidor de aplicação, muito
menos o uso de EJBs. Aumentando, com isso, a qualidade e integridade da camada
de negócios gerenciada pelo framework Spring. A seguir, falaremos do uso de
mensagens em uma arquitetura leve, novamente sem o uso de um servidor de
aplicação para o envio e o consumo de mensagens tanto síncronas com
assíncronas.

5.7. Uso de mensagens Assíncronas


Atualmente, o uso de mensagens assíncronas é bastante importante em
aplicações distribuídas, pois dependendo do tipo e complexidade da requisição feita

75
76

pelo cliente, a aplicação não pode ficar parada.


Na plataforma Java, as mensagens assíncronas (e as síncronas também) são
implementadas através da API13 Java Message Service (JMS). Esta API permite,
além da criação, envio, recebimento e leitura de mensagens, a comunicação entre
sistemas (aplicações ou módulos), bastando apenas que tenham um destinatário em
comum e uma mensagem compartilhada.
Ao contrário do que se pode imaginar, a JMS não é e-mail. Uma das grandes
diferenças entre a JMS e o e-mail é que com a JMS há a garantia de que o receptor
irá receber a mensagem, além de a JMS trabalhar na comunicação entre sistemas e
o e-mail na comunicação entre pessoas.
A arquitetura de uma JMS é dividida basicamente em três partes:
• JMS Providers: São servidores que implementam as interfaces da JMS.
É através deles que as mensagens passam de um cliente a outro. Há
vários JMS Providers, como MQSeries [MQS2006], SonicMQ [SON2006]
e o OpenJms [OPE2006], entre outros. Os providers são responsáveis
também por garantir que a mensagem será entregue a um cliente, mesmo
que ele não esteja disponível no momento do envio da mensagem.
• Mensagens: São os objetos que são trocados entre os clientes.
• Clientes JMS: São os sistemas (componentes) que recebem e/ou enviam
as mensagens. Estes clientes podem consumir tanto mensagens
síncronas como mensagens assíncronas. Vale ressaltar que dois clientes
não interagem diretamente entre si. Sempre haverá um provider entre
eles para transportar as mensagens.
A figura abaixo mostra como funciona a arquitetura básica de uma JMS:

Figura 5.4: Arquitetura básica de uma JMS

13
Conjunto de rotinas e padrões estabelecidos por um software para utilização de suas funcionalidades. Ela é
composta por uma série de funções acessíveis somente por desenvolvedores, e que permitem utilizar
características do software menos evidentes ao usuário tradicional.

76
77

Há também alguns elementos que se localizam entre o JMS Provider e os


clientes. Como exemplos desses elementos, pode-se citar o Message Driven Bean
(MDB), que é utilizado pelo EJB e já foi discutido no capítulo 3, e o Message Driven
Pojos14 (MDP), que é utilizado pelo Spring e pelo Java Connector Architecture
15
(JCA), que será visto na próxima seção.

5.7.1. Message Driven Pojos (MDP)


Como dito anteriormente, neste trabalho irá ser aprofundado o Message Driven
Bean (MDB) e o Message Driven Pojos (MDP). O MDB já foi discutido no capítulo 3,
mas vale ressaltar que não é possível enviar uma mensagem diretamente a um
MDB, e sim as mensagens são enviadas a um canal que o bean escuta.
Há duas formas de utilização de MDP: Através do Spring e através do JCA,
onde ambas se complementam e podem ser utilizadas conjuntamente.
Com JCA, o JCA container provê um eficiente gerenciamento de filas do JMS e
processa mensagens paralelamente.
Este processamento de mensagens paralelas (concorrentes) é uma das
principais características do JCA. Esse processamento é feito através de um pool de
conexões e sessões de JMS.
Com Spring, Craig Walls, autor de um livro sobre Spring (Spring in Action),
defende que o MDP é um MDB, porém para simples JavaBeans (classes simples de
Java). Com isso, o MDP pode utilizar todas as características providas pelo Spring.
Ao se fazer uma comparação entre MDP e MDB, pode-se dizer que a principal
diferença entre eles é que o MDB deve obrigatoriamente ser executado em um
container EJB, o que torna o MDB bastante aliado a arquitetura. Já o MDP pode ser
executado em qualquer arquitetura, bastando apenas que haja um JMS Provider.
Além disso, com o MDP, o ambiente de desenvolvimento não é composto por
servidores de aplicação. O Spring referencia o MDP, e um container Web (como o
Tomcat, por exemplo) possui um JMS Provider. Quando o JMS Provider recebe uma
mensagem, ela dispara essa mensagem para o MDP (gerenciado pelo Spring), que
por sua vez, processa a mensagem e envia para o cliente. O Spring acaba por atuar
como coordenador de todo o processo, pois é ele quem cria as filas e chama o MDP
14
POJOs são objetos Java que seguem a estrutura de um JavaBean, que não possuem nenhum conhecimento de
código de infra-estrutura.
15
O JCA provê uma solução para a conectividade entre os servidores de aplicação e os sistemas de informação
corporativos.

77
78

quando existirem mensagens. A figura 5.4 demonstra como funciona o processo.

Figura 5.5: Funcionamento do MDP

78
79

Capítulo 6

Realização de testes de carga em aplicações usando


arquiteturas EJB e arquiteturas leves em J2EE

Com o intuito de finalizar as discussões sobre a adoção ou não de novas


técnicas de arquiteturas, foram elaboradas duas aplicações usando as melhores
técnicas de padrões de projetos para uma aplicação usando EJB e outra usando o
framework Spring.

6.1. Testes de Carga

Teste de carga no servidor é comumente usado para mensurar a performance


de aplicações web. Testes de carga é o processo de simular requisições clientes,
assim o servidor pode experimentar um grande número de atividades de uma forma
controlada. Se a performance da aplicação falhar, passos devem ser tomados a fim
de descobrir tais problemas. Normalmente quem executa testes de carga são
ferramentas específicas para tal, como o JMeter, que é um projeto open-source de
testes de carga da Apache© [JME2006].
JMeter é uma ferramenta simples de utilizar, com uma interface amigável para
o usuário. Nele são gravadas as requisições da aplicação que se deseja testar, além
de se definir a quantidade de threads as quais serão executadas (trabalhando como
quantidade de usuários simultâneos que acessam a aplicação), e também se define
a quantidade de vezes que se deseja que as requisições sejam executadas.
A figura 6.1 mostra a interface do JMeter, executando uma das aplicações que
foi desenvolvida neste trabalho (a aplicação utilizando Spring, melhor explicada na
próxima seção). Ela demonstra o momento em que a aplicação obteve uma
estabilização no tempo de resposta, momento este que foram coletados os dados
para os gráficos mostrados na próxima seção.

79
80

Figura 6.1: Interface do JMeter

6.2. Importância dos Testes de Carga

O objetivo de testes de carga é determinar tanto a escalabilidade quanto a


performance de aplicações web. Escalabilidade é a habilidade de o sistema tratar o
aumento de carga sem ter uma grande perda de performance. Com isso, a
escalabilidade está relacionada diretamente com a performance.
Sem testes de carga, o administrador não consegue perceber se a aplicação é
tão escalável quanto ela deveria ser. Com o teste de carga, é possível saber em
quais pontos a aplicação pode apresentar falhas que podem comprometer a
performance da aplicação.
Para a simulação de uma aplicação real foi desenvolvido tanto um projeto
usando EJB quanto um projeto usando Spring. Tal projeto não tem como objetivo ser
uma aplicação amigável para o usuário, mas sim ser uma aplicação que realize
certas operações comuns em muitas aplicações web. O fluxo de ambos os sistemas
consiste no login de usuários previamente cadastrados. Posteriormente, o usuário é
levado para a página inicial da aplicação, onde o mesmo tem duas opções: ele pode

80
81

cadastrar um novo usuário, ou pode listar os usuários previamente cadastrados.


Com o fluxo presente, usamos o JMeter como um proxy para que gravasse o
fluxo de operações listadas acima. Uma vez gravado esse fluxo, iniciamos os testes
de carga. A ferramenta permite que as operações gravadas sejam repetidas por
diversas threads (cada thread equivale a um usuário), simulando, com isso, a carga
que o sistema pode ter. A figura 6.2 mostra o fluxo descrito acima.

Figura 6.2: Fluxo da aplicação desenvolvida

A tabela 6.1 e a figura 6.3 demonstram os resultados obtidos pela aplicação


usando Spring. O tempo do gráfico refere-se ao tempo total da execução do
processo total do sistema, que é de logar, cadastrar, listar, editar e excluir usuários.
O usuário, por sua vez, é a quantidade de usuários simultâneos que acessaram a
aplicação.

81
82

Aplicação usando Spring

Repetições 500 1000


Usuários

2 65 ms 106 ms
10 353 ms 1342 ms
20 708 ms 1975 ms
50 1745 ms 4012 ms
100 2900 ms 7600 ms
300 9253 ms 9996 ms
500 10652 ms 13056 ms
Tabela 6.1: Tabela comparativa na aplicação que utiliza Spring

Spring – Usuário/Tempo
Tempo (ms)
14000

12000

10000

8000 Série1
6000 Série2

4000

2000

0
0 200 400 600
Usuários

Figura 6.3: Gráfico de resultados obtidos na aplicação que utiliza Spring

Por sua vez, a tabela 6.2 e a figura 6.4 demonstram os resultados obtidos pela
aplicação usando EJB.

82
83

Aplicação usando EJB

Repetições 500 1000


Usuários

2 327 ms 255 ms
10 767 ms 1360 ms
20 1071 ms 2253 ms
50 3500 ms 4234 ms
100 7038 ms 7632 ms
300 10447 ms 12983 ms
500 14523 ms 13678 ms

Tabela 6.2: Tabela comparativa na aplicação que utiliza EJB

EJB – Usuário/tempo
Tempo (ms)
16000
14000
12000
10000
Série1
8000
Série2
6000
4000
2000
0
2 10 20 50 100 300 500
Usuários

Figura 6.4: Gráfico de resultados obtidos na aplicação que utiliza EJB

Com a realização dos testes, pôde-se notar que não há uma grande diferença
entre a performance das duas aplicações. É importante salientar que os testes
foram realizados em computadores comuns. Foi usado um Notebook Apple Power
Book 1,64 GHz com 512 MB de Memória, e um Notebook Toshiba 2,3 GHz com 512

83
84

MB de memória. Ou seja, não demonstram a plataforma de hardware real de


servidores de aplicações, servindo, então, apenas de amostragem para fins
didáticos do presente trabalho.

84
85

Conclusão

O presente trabalho mostrou diversos aspectos de arquiteturas de sistemas


distribuídos (ou não) usando a plataforma J2EE. Mostrou a importância de se
planejar uma arquitetura para sistemas que tendem a crescer ao longo dos tempos,
disponibilizando, com isso, conceitos como escalabilidade, performance,
gerenciabilidade e disponibilidade.
Porém, foi evidenciado que a forma tradicional de se desenvolver aplicações
distribuídas usando a plataforma J2EE é bastante burocrática e pode impactar no
sucesso de sistemas que não possuem profissionais experientes, além de outros
fatores mencionados. Com tais complexidades, o uso de padrões de projeto em
aplicações J2EE normalmente dita o fracasso do projeto caso sejam utilizados de
forma incorreta, adicionando ainda mais complexidade na arquitetura.
Diante desses fatores, foram surgindo alternativas e novos conceitos para o
desenvolvimento de sistemas escaláveis, entretanto simples.
Procurou-se mostrar novas técnicas usadas para a disponibilização de serviços
e conceitos presentes na arquitetura robusta J2EE, como o IoC, que ocasiona um
fraco acoplamento entre camadas, e o AOP, que proporciona diversas
funcionalidades como segurança e gerenciamento declarativo de transações fora de
um servidor de aplicação.
Finalmente foi apresentado de forma bastante breve um teste de carga para
evidenciar a performance e a escalabilidade de ambos os sistemas.
Diante dos estudos e testes realizados, os autores acreditam que as novas
aplicações usando frameworks leves são uma alternativa viável para arquiteturas
que necessitem ser escaláveis e robustas. Porém, não se pode descartar por
completo aplicações tradicionais J2EE, pois existe um enorme legado e para
aplicações que necessitem ser realmente distribuídas o uso de EJBs ainda é
recomendado.
No presente trabalho, com a apresentação de arquiteturas leves, torna-se
interessante em trabalhos futuros o aprofundamento de um estudo dessas
arquiteturas para que cada vez mais ela possa prover serviços ainda presentes na
arquitetura somente na arquitetura J2EE.

85
86

Referências Bibliográficas

[ALL2003] ALLEN, Paul; BAMBARA, Joseph. Sun Certified Enterprise


Architect for J2EE - Guia Oficial de Certificação. Editora Campus,
2003. 648 p.

[ASP2005] AspectWerkz - Plain Java AOP. Disponível em:


<http://aspectwerkz.codehaus.org>. Acesso em: Dezembro de 2005.

[ASP2006] Site oficial do projeto AspectJ. Disponível em:


<http://www.eclipse.org/aspectj>. Acesso em: Janeiro de 2006.

[AST1998] ASTUDILLO, Hernán; HAMMER, Stuart. Understanding the


Architect's Job. Software Architecture Workshop of OOPSLA'98,
1998.

[BAS1998] BASS, Len; CLEMENTS, Paul; KAZMAN, Rick. Software


Architecture in Practice. Addison Wesley, 1.ed. 1998, 512 p.

[BEC1996] BECK, Kent. Smalltalk Best Pratice Patterns. 1.ed. Prentice Hall
PTR, 1996, 240 p.

[BEN1996] BENNETT, Douglas. Designing Hard Software: The Essential


Tasks. Manning Publications Co., 1996, 350 p.

[CRI2000] CRISPIN, Lisa; WADE, Carol. The Need for Speed: Automating
Acceptance testing in an Extreme Programming Environment.
Disponível em:
<http://www.soft.com/QualWeek/QWE2K/Papers.pdf/Crispin.pdf>.
Acesso em: Dezembro de 2005.

86
87

[DAN2006] ROCHA, André Dantas; SIMÃO, Adenilso da Silva; MALDONADO,


José Carlos; MASIERO, Paulo César. Uma ferramenta baseada em
aspectos para o teste funcional de programas Java. Laboratório
de Engenharia de Software, Instituto de Ciências Matemáticas e de
computação da Universidade de São Paulo.

[FOW2004] FOWLER, Martin. Inversion of Control Containers and the


Dependency Injection pattern. Disponível em:
<http://www.martinfowler.com/articles/injection.html>. Acesso em:
Fevereiro de 2006.

[HAR2005] HARROP, Rob; MACHACEK, Jan. Pro Spring. Apress. 2005, 832 p.

[HIB2006] Site oficial do framework Hibernate. Disponível em:


<http://www.hibernate.org>. Acesso em: Janeiro de 2006

[IBA2006] Site oficial do framework IBatis. Disponível em:


<http://www.ibatis.org>. Acesso em: Janeiro de 2006.

[JAZ2000] JAZAYERI, Mehdi; RAN, Alexander; LINDEN, Frank van der.


Software Architecture for Product Families. Addison Wesley, 2000,
257 p.

[JBO2005] Site oficial do framework JBoss AOP. Disponível em:


<http://labs.jboss.com/portal/jbossaop>. Acesso em: Dezembro de
2005.

[JDB2006] Site oficial da API JDBC. Disponível em:


<http://java.sun.com/products/jdbc/>. Acesso em: Janeiro de 2006.

[JME2006] Site oficial da ferramenta JMeter. Disponível em:


<http://jakarta.apache.org/jmeter/>. Acesso em: Março de 2006.

87
88

[JOH2004] JOHNSON, Rod; HOELLER, Juergen. Expert One-on-One J2EE


Development without EJB. Wiley Publishers. 2004, 576 p.

[JTE2006] Site oficial da ferramenta JTest. Disponível em:


<http://www.parasoft.com/jtest>. Acesso em: Fevereiro de 2006.

[JUN2006] Site oficial do framework JUnit. Disponível em:


<http://www.junit.org>. Acesso em: Fevereiro de 2006.

[LEI2005] LEITE, Alessandro Ferreira. Padrões de Projeto. Disponível em:


<http://www.devmedia.com.br/visualizacomponente.aspx?comp=957&
site=3>. Acesso em: Dezembro de 2005.

[LOZ2003] LOZANO, Fernando. Patterns e anti-patterns para o


desenvolvimento em PHP. Disponível em:
<http://www.lozano.eti.br>. Acesso em: Dezembro de 2005.

[MAC2005] MACORATTI, José Carlos. Padrões de Projeto - Design Patterns.


Disponível em: <http://www.macoratti.net/vb_pd1.htm>. Acesso em:
Dezembro de 2005.

[MAI2005] MAIORIELLO, James. What are Design Patterns and do I need


them? Disponível em:
<http://www.developer.com/design/article.php/1474561>. Acesso em:
Dezembro de 2005.

[MAR1996] MARTIN, Robert C. The Dependency Inversion Principle.


Disponível em:
<http://www.objectmentor.com/resources/articles/dip.pdf>. Acesso em:
Janeiro de 2006.

88
89

[MAL2005] MALARVANNAN, Mani. Design Better Software with the Inversion


of Control Pattern. Disponível em:
<http://www.devx.com/Java/Article/27583>. Acesso em: Janeiro de
2006.

[MQS2006] Site oficial do MQSeries. Disponível em:


http://www.ibm.com/software/mqseries/. Acesso em: Janeiro de 2006.

[OPE2006] Site oficial do OpenJMS. Disponível em:


<http://openjms.sourceforge.net>. Acesso em: Janeiro de 2006.

[PER2006] Site oficial do framework Perola. Disponível em:


<http://sourceforge.net/projects/perola/>. Acesso em: Janeiro de 2006.

[PRE2001] PRESSMAN, Roger. Software Engineering: A Practitioner's


Approach. McGraw Hill, 5.ed. 2001.

[ROM2004] ROMAN, Ed; SRIGANESH, Rima Patel; BROSE, Gerald. Mastering


Enterprise JavaBeans. Wiley Publishers. 3.ed. 2004, 839 p.

[SER2005] NENE, Dhananjay. A beginners guide to Dependency Injection.


Disponível em:
<http://www.theserverside.com/articles/article.tss?l=IOCBeginners>.
Acesso em: Janeiro de 2006.

[SER2006] HELLESOY, Aslak. Tech Talk Vídeo. Disponível em:


<http://www.theserverside.com/talks/videos/AslakHellesoy/interview.ts
s?bandwidth=real>. Acesso em: Fevereiro de 2006.

[SHA1996] SHAW, Mary; GARLAN, David. Software Architecture Perspectives


on an Emerging Discipline. 1.ed. Prentice Hall, 1996, 242 p.

[SIE2004] SIERRA, Kathy; FREEMAN, Elisabeth; BATES, Bert; FREEMAN, Eric.


Head First Design Patterns. 1.ed. O'Reilly Media, Inc, 2004. 676 p.

89
90

[SON2006] SonicMQ: OpenEdge Integration Portfolio. Disponível em:


<http://www.progress.com>. Acesso em: Janeiro de 2006.

[SPR2006] Site oficial do framework Spring. Disponível em:


<http://www.springframework.org/>. Acesso em: Fevereiro de 2006.

[SUN2006] BALL, Jennifer; CARSON, Debbie Bode; EVANS, Ian; HAASE, Kim;
JENDROCK, Eric. The Java EE 5 Tutorial. Disponível em:
<http://java.sun.com/javaee/5/docs/tutorial/doc/>. Acesso em:
Fevereiro de 2006.

[TES2006] Site oficial da ferramenta TestComplete. Disponível em:


<http://www.automatedqa.com/products/testcomplete/index.asp>.
Acesso em: Fevereiro de 2006.

[WAL2005] WALLS, Craig; BREIDENBACH, Ryan. Spring in Action. Manning


Publications Co. 2005, 472 p.

[WEL2005] WELLS, Don. Extreme Programming: A gentle introduction.


Disponível em: <http://www.extremeprogramming.org>. Acesso em:
Novembro de 2005.
[WIK2006] Wikipedia, the free encyclopedia. Disponível em:
<http://en.wikipedia.org>. Acesso em: Março de 2006.

[XDE2006] Site oficial da ferramenta XDE Tester. Disponível em: <http://www-


306.ibm.com/software/awdtools/tester/functional/>. Acesso em:
Fevereiro de 2006.

90

You might also like