a r t i g o
Eduardo Guerra
([email protected]) é pesquisador em
desenvolvimento de frameworks, participando de
projetos open-source como SwingBean, Esfinge
Framework, ClassMock e JColtrane. Atualmente
está cursando doutorado no ITA, onde também
já concluiu graduação em Engenharia da
Computação e mestrado. Possui as certificações
SCJA, SCJP, SCWCD, SCBCD (1.3 e 5.0), SCJWSD,
SCMAD e SCEA e experiência como arquiteto de
software nas plataformas Java SE, Java EE e Java ME.
Atua também como professor na graduação do ITA
e nos cursos de pós-graduação ITA/Stefanini.
Roberto Perillo
([email protected]) é bacharel em Ciência
da Computação e está atualmente concluindo o
curso de especialização em Engenharia de Software
do ITA. Trabalha com Java há quase 5 anos, possui
as certificações SCJP, SCWCD e SCJD, e participa
ativamente dos fóruns do JavaRanch. Já trabalhou
com JEE em grandes empresas, como Avaya e
IBM, onde nesta última foi co-fundador do time de
inovação de ibm.com GWPS Brasil.
Uma história de padrões é uma narrativa
que apresenta um problema fictício e como
padrões foram aplicados de forma sequencial
para sua solução. Uma história interativa de
padrões é uma narrativa na qual o leitor vai
tomando as decisões de design e conhecendo
as consequências de suas ações. Este artigo traz
uma história interativa de padrões para camada
de negócios em aplicações Java EE. Será que você
consegue tomar as decisões corretas?
Aprenda sobre padrões Java
EE através das consequências
de suas próprias escolhas
em uma história de padrões
interativa, na qual o final é
você quem decide.
E ste ano tive o prazer de participar de uma das mais importan-tes conferências sobre padrões do mundo, o PLoP 09 (16th Conference on Pattern Languages of Programs). O evento
contou com grandes nomes na área de padrões e modelagem de software como Ralph Johnson, Rebecca Wirfs-Brock, Joseph Yoder, Brian Foote, Lin-da Rising, entre outros. Neste ambiente, me deparei com um artigo sobre padrões bem diferente do tipo de artigo que estava acostumado a ver.
Até então eu já conhecia as histórias de padrões (pattern stories) que apresentam uma narrativa que mostra um caso fictício e como os padrões são aplicados de forma incremental. Essas histórias são interessantes, pois ensinam sobre a aplicabilidade dos padrões e as consequências positivas e negativas de sua aplicação. O artigo “An Interactive Pattern Story about Remote Object Invocation” publicado este ano (pode ser baixado do site da
Aprendendo Padrões Java EE com
uma História Interativa
1818 www.mundoj.com.br
1919
conferência) trouxe um conceito inovador no qual o leitor pode interagir com a história tomando decisões de design e encarando as consequências de suas escolhas.
Seguindo o mesmo conceito da série de livros “Escolha sua Aventura”, as histórias interativas de padrões fornecem ao leitor escolhas de design que influenciam o final da história. Diferentemente das histórias normais que documentam apenas escolhas corretas, as histórias interativas permitem que os leitores explorem as soluções não-ótimas e identifiquem as conse-quências negativas da aplicação de um padrão em um contexto em que sua utilização não é apropriada.
Este artigo apresenta uma história interativa que foca em padrões de pro-jeto para a camada de negócios de aplicações Java EE. A próxima seção explica como será a dinâmica da leitura do artigo e em seguida o leitor já pode iniciar a leitura da história e tomar suas próprias decisões. No final do artigo, existem quadros explicativos que focam em algumas das escolhas e tecnologias relacionadas com o andamento da história, porém que podem ser lidos de forma independente.
Como ler o artigoEra uma vez...
A história deste artigo não segue um formato linear, mas possui um
formato de grafo, no qual existem pontos de decisão que podem levar a
diferentes finais. Ao ler cada seção, o leitor deve decidir para que ponto
da história ele deve seguir e então pular para seção numerada de acordo
com a decisão tomada. Existe um final “feliz” no qual o leitor tomou todas
as decisões corretas, porém também existe o final desastroso em que
todas as decisões tiveram consequências ruins. Alguns finais também
retratam situações intermediárias nas quais diferentes decisões tiveram
consequências boas e ruins em diferentes pontos.
O objetivo é que o leitor interprete os requisitos que estão sendo
apresentados e procure tomar a melhor decisão para a modelagem da
Seu nome é Edward De Sign e você acabou de ser contratado para o cargo
de arquiteto Java EE da empresa AvajOdnum. Essa empresa multinacional
indiana possui uma grande aplicação que roda na internet e provê serviços
para diversas empresas que pagam pela sua utilização. Não foram dados
muitos detalhes no momento de sua contratação, mas parece que sua
primeira tarefa será o projeto de um módulo para a inserção de notifica-
ções de chamadas em métodos já existentes na aplicação. A vaga foi bem
disputada, então você, de alguma forma, sente o peso da responsabilidade
para a geração de um resultado positivo o mais rápido possível.
Pronto para o primeiro dia de trabalho? Então vá para (1).
arquitetura da aplicação. Cada decisão terá consequências em termos de requisitos não-funcionais. Incentiva-se que o leitor, depois da primeira leitura, explore os diferentes caminhos que a história proporciona, para entender qual seria o resultado caso outras decisões fossem realizadas.
Em cada ponto da história será apresentada uma figura que irá mostrar como a arquitetura se encontra naquele momento. Essa figura serve como base para que o leitor possa ter uma visão de como se encontra o projeto que está sendo realizado.
No final do artigo, existem alguns quadros explicativos para os quais o leitor será direcionado em alguns pontos de sua leitura. Esses quadros possuem informações comuns a diversos ramos da história e que podem ser lidos independentemente do seu andamento. Nesses quadros estão detalhes a respeito das tecnologias utilizadas e dos padrões emprega-dos. Caso o leitor já conheça o padrão ou a tecnologia que estiver sendo abordada no quadro, ele pode pular essa leitura sem prejuízo ao anda-mento da história.
Ciente das regras? A história começa na próxima seção...
Você chega na empresa no seu primeiro dia e é recebido com muito entusiasmo e expectativa por seus novos colegas. Na parte da manhã, você é apresentado à sua equipe de trabalho e se acomoda na sala onde irá trabalhar, realizando tarefas de um recém-chegado como criação de credenciais de segurança e configuração de ambiente. Na parte da tarde, você é cha-mado a uma reunião na qual será explicada a arquitetura do sistema com o qual irá trabalhar e qual será sua primeira tarefa.
Quando já estava esperando que a arquitetura do sistema com o qual irá trabalhar fosse “arcaica” e amarrada a antigos padrões de mercado, você se depara com uma boa surpresa: o sistema está implementado utilizando EJB 3 e JPA. O sistema possui um módulo web implementado em JSF e um módulo desktop im-plementado em Swing que utilizam as interfaces remotas dos EJBs de sessão para o acesso as funcionalidades do sistema. No servidor de aplicações, existe uma camada de EJBs destinada a realizar o acesso a base de dados utilizando JPA, funcionando como uma camada de DAOs. Acima dessa camada, existem EJBs que executam regras de negócio das funcionalidades e acessam a camada de EJBs de acesso a dados quando neces-
sário. As camadas de EJBs para acesso a dados disponibilizam apenas interfaces locais, enquanto a outra camada apenas interfaces remotas.
Devido ao fato da aplicação ser disponibilizada para outras empresas,
diversas necessidades específicas surgem em cada um dos clientes,
muitas vezes relacionadas com a integração com sistemas existentes.
Esse tipo de requisição motivou a gerência a dar importância para a
implementação de um mecanismo na arquitetura que permitisse que
funcionalidades fossem invocadas antes ou depois da chamada de
funcionalidades na camada de negócios, ou seja, nos EJBs remotos da
aplicação.
Não é necessário que essas chamadas influam na execução da funciona-
lidade. A princípio elas apenas devem notificar a execução da funciona-
Aplicação Swing
Aplicação JSF
chamada remota
Session Beans
(negócio)
Session Beans (DAO)
2020 www.mundoj.com.br
lidade. Exemplos de uso desse novo recurso citados na reunião foram:
gravação de trilha de auditoria em bancos de dados ou em arquivos,
acionamento de gatilhos de processos em servidores de workflow, cha-
madas a sistemas externos e envios de e-mail de notificação.
O projeto de como isso será implementado na aplicação será seu pri-
meiro desafio nesse cargo de arquiteto. O projeto se dividirá em três
fases, nas quais haverá a implementação e em seguida a implantação
no servidor em produção. Segue o que será abordado em cada fase:
– Integração do mecanismo de notificação nas funcionali-
dades existente;
– Definição da forma de tratamento das notificações;
– Flexibilização do mecanismo para inserção de novas no-
tificações.
A primeira questão que deverá ser tratada é como a notificação deverá
ser integrada na aplicação já existente de forma a gerar o menor im-
pacto possível no que já está implementado. Neste cenário, surgem
algumas alternativas a serem seguidas:
deseja se reunir com a equipe para explorar melhor os requisitos não-funcionais, vá para (4);
utilizando a injeção de dependência do EJB container para injetar um Session Bean que deve ser chamado dentro dos métodos para que a notificação seja feita, vá para (2);
para executarem em torno da execução dos métodos do EJBs, vá para (7).
2 Você decide que o mecanismo de notificação será imple-mentado em um Session Bean, chamado de Notificador, o qual deve ser injetado pelo container dentro dos EJBs já existentes na aplicação (para mais informações, ler quadro “O Padrão Injeção de Dependência”). Dentro dos métodos dos EJBs, deve ser feita uma chamada a métodos do Noti-ficador passando as informações do contexto da chamada do método, como o seu nome, o EJB ao qual pertence, os parâmetros recebidos e o usuário que o executou.
Para a implantação desse primeiro passo foi escolhida uma fun-
cionalidade simples, porém importante, para ser implantada: o
registro de auditoria em arquivo (ou seja, a gravação do log da
execução das funcionalidades do sistema). Ela servirá para tes-
tar se o mecanismo de notificação está corretamente inserido
na aplicação para que outras questões possam ser tratadas.
O Session Bean Notificador foi implementado facilmente para
realizar as tarefas de auditoria. Para que o mesmo possa ser
utilizado por outros beans, bastou utilizar a injeção de depen-
dência do container. Um atributo do tipo da interface do No-
tificador com a anotação @EJB foi inserido nos session beans
que precisavam utilizar a auditoria. A chamada ao Notificador
precisou ser inserida em cada um dos métodos de cada um dos EJBs. Esta
estratégia se mostrou flexível devido a facilidade de adicionar a chamada
em qualquer ponto da execução do método. Um ponto negativo foi que,
apesar da lógica de processamento estar no bean Notificador, ele ainda
precisa ser invocado dentro de cada método, o que gerou um maior aco-
plamento e um enorme trabalho braçal por parte dos desenvolvedores.
Foi um pouco mais de uma semana de trabalho intenso de toda equipe
para inserir o mecanismo em toda a aplicação. Mesmo com tanto esforço,
alguns métodos acabaram sendo esquecidos e outros implementados
de forma errada. Depois da implementação estar pronta, ainda foi mais
uma semana com diversos testes cobrindo toda a aplicação, nos quais
diversas pequenas falhas foram detectadas. Mesmo depois dos testes,
você ainda se sente um pouco inseguro se está tudo realmente correto.
A equipe ficou insatisfeita em precisar executar um trabalho braçal em
toda a aplicação. A gerência esperava que a implantação desse mecanis-
mo inicial fosse mais rápida e ficou decepcionada com o número de erros
que foram encontrados na fase de testes. Talvez a solução escolhida não
tenha sido a ideal, porém depois de tanto trabalho não dava mais para
voltar atrás. Você sente que suas decisões em relação a outras questões
do mecanismo de notificação serão críticas para seu futuro na empresa.
O bean notificador agora está implementando diretamente a funcionali-
dade de auditoria, mas a intenção é que ele seja apenas um gerenciador
que recebe essas chamadas e redireciona para quem deve fazer o trata-
mento. Outro aspecto que deve ser considerado é a transacionalidade,
pois a notificação só deve ser feita caso a funcionalidade seja executada
com sucesso. Neste momento, existem duas opções a respeito de como
as notificações serão redirecionadas:
-síncrona para um Message-Driven Bean tratar, vá para (5);
para funcionalidade de notificação e delegar a execução da lógica para classes auxiliares, vá para (8).
Aplicação Swing
Aplicação JSF
chamada remota
Session Beans
(negócio)
Session Beans (DAO)
Session Beans
(Notificador)
como
-r, vá para (5);
e delegar a execução da lógica (8).
2121
3
4
A decisão tomada acaba dando um peso maior para sua preocupação com desempenho e você decide realizar uma chamada assíncrona para tratar a notificação, o que é um padrão conhecido como Service Activator (ver quadro “Padrão Service Activator”). Dessa forma, o interceptor irá enviar uma mensagem para um servidor de mensagens, que será recebida por um Message-driven Bean, o qual será responsável pelo processamento da notificação.
Antes de tomar uma decisão a respeito de como deve ser a implemen-tação, você decide reunir a equipe e realizar um brainstorm a respeito de quais os requisitos não-funcionais mais importantes. Além de obter informações importantes sobre os requisitos, você também ganhou o respeito da equipe por tê-la incluído no processo de modelagem da arquitetura.
No final da reunião foram sumarizados os seguintes requisitos não-funcionais:
a aplicação executa atualmente no limite máximo do desempenho que é permitido pelos requisitos e alguns clien-tes chegam a reclamar de lentidão em horários de pico. Não é de-sejável que o mecanismo de notificação cause uma degradação perceptível no desempenho das funcionalidades já existentes.
o sistema atual é muito grande e uma imple-mentação que criasse a necessidade de alteração em diversas classes do sistema seria muito cara e trabalhosa. O mecanismo de notificação deve ser o mais desacoplado possível das classes já existentes no sistema.
diversos clientes que utilizam o sistema já comen-
taram a respeito da necessidade do envio de notificações para sistemas proprietários. Quando o mecanismo estiver pronto, será necessário a implementação de diferentes tipos de notificações para os diferentes clientes. Sendo assim, o processo de criação de tipos de notificação deve ser simples, flexível e configurável.
uma notificação só pode ser efetuada caso a funcionalidade seja executada sem que tenha sido dado um roll-back na transação.
Depois da conversa com a equipe, você se sente mais confiante e com mais informações para tomar decisões a respeito da arquitetura do sistema. Qual será sua decisão a respeito de como o mecanismo de notificação deve ser integrado ao sistema?
utilizando a injeção de dependência do EJB container para injetar um Session Bean que deve ser chamado dentro dos métodos para que a notificação seja feita, vá para (2).
para executarem em torno da execução dos métodos do EJBs, vá para (7).
Como forma de testar esse novo passo do desenvolvimento
será implantada a primeira integração com o sistema de um
dos maiores clientes da AvajOdnum. O cliente disponibilizou
um web service em um dos seus sistemas para ser invocado na
chamada de algumas funcionalidades. Não se sabe exatamente
qual funcionalidade será executada pela aplicação dele. A
princípio serão utilizadas condicionais simples para definir quais
chamadas deverão invocar o serviço do cliente, porém isso deve
ser mudado na última fase da implantação do mecanismo de
notificações.
A funcionalidade de auditoria foi transferida para um Message-
driven Bean que trata mensagens recebidas de uma fila do
servidor de mensagens. O envio da mensagem é feito pelo
interceptor Notificador que antes implementava a funcionali-
dade diretamente. A chamada ao web service do cliente é feita,
a princípio, diretamente do Message-driven Bean e o filtro por
funcionalidade é feito utilizando condicionais no próprio código.
A criação da funcionalidade foi rapidamente feita por você mesmo e nos testes realizados não houve degradação do desempenho da aplicação. Em alguns períodos do dia o serviço do cliente acaba ficando um pouco lento e demorando a responder, porém como isso é tratado assincrona-mente, esse fato não interfere no tempo de execução percebido pelos usuários. O envio da mensagem é incluído na transação distribuída do container e só é efetivado caso não seja dado rollback na mesma. Isso é especialmente útil para notificações realizadas antes da execução da funcionalidade, quando no momento da notificação ainda não se sabe se haverá algum problema que poderá anular a transação.
Havia certo receio na equipe e na gerência que as chamadas a funcionalidades de outras aplicações fossem degradar o desempenho, porém com a solução da chamada assíncrona você mostrou que é possível fazer isso mantendo o tem-po de resposta da aplicação. Você ganha o respeito de todos, que depositam em você a confiança para concluir a funcionalidade de notificação.
A próxima decisão a ser tomada irá influenciar a facilidade de adição de novas ações a serem acionadas pela notificação. No momento, estão implementadas as funcionalidades de auditoria e a chamada a aplicação de um dos clientes, porém você sabe que várias outras ações serão inseridas no futuro. Atualmente condicionais no próprio código verificam as condições de execução de cada tipo de notificação, porém você deve criar um mecanismo que facilite a adição desse tipo de funcionalidade. É preciso levar em consideração que mais de uma ação pode ser realizada a partir de uma notificação e que pode haver lógicas comuns em funcionalidades de notificação diferentes. Qual será a estratégia que você irá escolher para fazer isso?
no método onMessage() que chama métodos abstratos para serem im-plementados por várias subclasses usando o padrão Template Method, vá para (13).
objetos encadeados para o tratamento das mensagens usando o padrão Chain of Responsibility, vá para (17).
Aplicação Swing
Aplicação JSF
chamada remota
Servidor de Mensagens
Processador de Notificações
(Message-driven Bean)
Not
ifica
dor
(inte
rcep
tor)
Session Beans
(negócio)
Session Beans (DAO)
2222 www.mundoj.com.br
5
6
A decisão tomada acaba dando um peso maior para sua preocupação com desempenho e você decide realizar uma chamada assíncrona para tratar a notificação, o que é um pa-drão conhecido como Service Activator (ver quadro “Padrão Service Activator”). Dessa forma, o Session Bean Notificador irá enviar uma mensagem para um servidor de mensagens, que será recebida por um Message-driven Bean, o qual será responsável pelo processamento da notificação.
Quando você entra na sala do gerente, ele está trabalhando em
seu computador. Ele a princípio parece ignorar sua presença, mas
em seguida volta-se para você encarando-o com uma expressão
bem séria. Para sua felicidade, ele alivia a pressão do momento
com um sorriso e pede para você se sentar. Após fazer algumas
perguntas sem muita importância para quebrar o gelo, ele diz que
o motivo de ter te chamado era para falar do seu desempenho até
o momento e falar sobre o seu futuro na empresa:
ficou dentro de nossas expectativas iniciais. Apesar de você
ter tomado decisões inteligentes e que realmente ajudaram
na implementação do mecanismo de notificação de um de
nossos sistemas, você mesmo sabe que essas decisões não
foram sempre corretas que houveram algumas consequên-
cias negativas.
Você balança a cabeça admitindo que nem todas suas decisões
foram as melhores e ele continua:
outra empresa e precisaremos integrar os nossos sistemas
com os sistemas deles. Para isso, estamos criando a posição
de arquiteto corporativo em nossa empresa. Queremos que
você continue atuando como arquiteto nessa aplicação e que
interaja com o novo arquiteto corporativo nessa integração.
Como forma de testar esse novo passo do desenvolvimento
será implantada a primeira integração com o sistema de um
dos maiores clientes da AvajOdnum. O cliente disponibilizou
um web service em um dos seus sistemas para ser invocado
na chamada de algumas funcionalidades. Não se sabe exa-
tamente qual funcionalidade será executada pela aplicação
dele. A princípio serão utilizadas condicionais simples para
definir quais chamadas deverão invocar o serviço do cliente,
porém isso deve ser mudado na última fase da implantação
do mecanismo de notificações.
A funcionalidade de auditoria foi transferida para um Mes-
sage-driven Bean que trata mensagens recebidas de uma
fila do servidor de mensagens. O envio da mensagem é feito
pelo Session Bean Notificador que antes implementava a
funcionalidade diretamente. A chamada ao web service do
cliente é feita, a princípio, diretamente do Message-driven
Bean e o filtro por funcionalidade é feito utilizando condicionais no
próprio código.
A criação da funcionalidade foi rapidamente feita por você mesmo e nos
testes realizados não houve degradação do desempenho da aplicação.
Em alguns períodos do dia o serviço do cliente acaba ficando um pouco
lento e demorando a responder, porém como isso é tratado assincrona-
mente, esse fato não interfere no tempo de execução percebido pelos
usuários. O envio da mensagem é incluído na transação distribuída do
container e só é efetivado caso não seja dado rollback na mesma. Isso
é especialmente útil para notificações realizadas antes da execução da
funcionalidade, quando no momento da notificação ainda não se sabe
se haverá algum problema que poderá anular a transação.
Havia certo receio na equipe e na gerência que a chamada a funciona-
lidade de outras aplicações fosse degradar o desempenho, porém com
a solução da chamada assíncrona você mostrou que é possível fazer
isso mantendo o tempo de resposta da aplicação. Com essa solução,
você recupera o respeito da equipe e da gerência que havia em parte
sido perdido depois da implantação da primeira parte do mecanismo de
notificação. Você sente que seu sucesso na próxima decisão será crítico
para seu futuro na empresa.
A próxima decisão a ser tomada irá influenciar a facilidade de adição de
novas ações a serem acionadas pela notificação. No momento, estão
implementadas as funcionalidades de auditoria e a chamada a aplicação
de um dos clientes, porém você sabe que várias outras ações serão in-
seridas no futuro. Atualmente condicionais no próprio código verificam
as condições de execução de cada tipo de notificação, porém você deve
criar um mecanismo que facilite a adição desse tipo de funcionalidade. É
preciso levar em consideração que mais de uma ação pode ser realizada
a partir de uma notificação e que pode haver lógicas comuns em funcio-
nalidades de notificação diferentes. Qual será a estratégia que você irá
escolher para fazer isso?
-ritmo no método onMessage() que chama métodos abstratos para serem implementados por várias subclasses usando o padrão Tem-plate Method, vá para 16.
outros objetos encadeados para o tratamento das mensagens usando o padrão Chain of Responsibility, vá para 11.
Aplicação Swing
Aplicação JSF
chamada remota
Servidor de Mensagens
Processador de Notificações
(Message-driven Bean)
Session Beans
(Notificador)
Session Beans
(negócio)
Session Beans (DAO)
2323
fim
7 Você decide criar um interceptor, que é uma classe que pode interceptar chamadas de um EJB, para a criação do mecanismo de notificação (para mais informações, ler quadro “EJB3 Interceptors”). Essa classe deve ser definida no descritor XML da aplicação como um interceptor de-fault para todos os Session Beans.
É muito importante que você sempre discuta com ele as mudanças que você fizer na arquitetura, pois ele terá uma visão mais global de nossas aplicações.
Após a conversa, você volta para sua mesa em parte feliz, porque apesar
do erro que você cometeu, ainda irá manter sua posição como arquiteto.
Por outro lado, você percebe que se não fosse a decisão errada que to-
mou, poderia ser você a assumir o cargo de arquiteto corporativo. Você
segue em frente, esperando que o desafio da integração dos sistemas
e a troca de experiências com o novo arquiteto corporativo possam te
ensinar bastante a respeito da modelagem da arquitetura de sistemas.
Para a implantação desse primeiro passo foi escolhida
uma funcionalidade simples, porém importante, para ser
implantada: o registro de auditoria em arquivo (ou seja, a
gravação do log da execução das funcionalidades do siste-
ma). Ela servirá para testar se o mecanismo de notificação
está corretamente inserido na aplicação para que outras
questões possam ser tratadas.
O interceptor, chamado de Notificador, foi implementado
facilmente para realizar as tarefas de auditoria e através das
configurações ele passou a ser chamado antes e depois da
invocação de todos os métodos. Uma das vantagens dessa
abordagem foi o baixo acoplamento desse novo mecanis-
mo com os Session Beans já existentes na aplicação, que
não precisaram ser alterados. Um problema encontrado foi
a falta de flexibilidade, pois devido a mesma classe inter-
ceptar todos os métodos fica difícil de saber, por exemplo,
se a notificação deve ser feita antes ou depois da execução
do método.
Para a resolução desse problema, seguindo a filosofia da ar-
quitetura do EJB 3, foram criadas anotações @NotifyBefore,
@NotifyAfter e @NoNotification que permitiam configurar
quando uma notificação seria enviada. Para evitar configu-
rações em todos os métodos, a notificação depois da exe-
cução, que era o caso mais comum foi considerado o caso
default. As anotações também poderiam ser utilizadas na
classe, servindo, nesse caso, como uma configuração para
todos os métodos, a não ser que existisse uma anotação
Se você se interessou sobre como um componente consome e pro-cessa os metadados que lê, isso já é outra história! Você pode ver a história resumida no artigo “Padrões de Projeto para Flexibilizar o Uso de Anotações” da edição 34 da revista Mundoj, ou a história completa no artigo “A Pattern Language for Metadata-based Fra-meworks” publicado no PLoP deste ano.
Não se pode dizer que foi rápido para inserir as configurações em toda a aplicação, porém o que deu mais trabalho foi percorrer todas as classes de serviço analisando se a notificação era necessária e em que momento ela deveria ser realizada. Em dois dias de dedicação da equipe de de-senvolvimento, o sistema de notificação já estava implantado em toda a aplicação e em mais dois dias os testes revelaram que a implementação havia sido um sucesso.
Todos da equipe ficaram surpresos e satisfeitos com a solução, pois nenhum deles havia pensado na solução de usar uma combinação dos interceptors com as anotações. O código da notificação ficou bem desacoplado das classes da aplicação e a manutenção do mecanismo e inserção da notificação em novos Session Beans serão tarefas bem tranquilas. A gerência ficou satisfeita com o resultado da sua primeira semana de trabalho, pois eles não esperavam que a inserção desse novo componente na arquitetura já existente fosse ser tão rápida. Você fica feliz com seu primeiro acerto, porém sabe que o mecanismo não está completo e ainda existem várias decisões pela frente.
O interceptor Notificador agora está implementando diretamente a funcionalidade de auditoria, mas a intenção é que ele seja apenas um gerenciador que recebe essas chamadas e redireciona para quem deve fazer o tratamento. Outro aspecto que deve ser considerado é a transa-cionalidade, pois a notificação só deve ser feita caso a funcionalidade seja executada com sucesso. Neste momento, existem duas opções a respeito de como as notificações serão redirecionadas:
-síncrona para um Message-Driven Bean tratar, vá para (3).
Session Bean que serve como fachada para funcionalidade de noti-ficação, vá para (10).
específica nele. Esse tipo de estratégica no uso de anotações é utilizada no EJB 3 para gerenciamento de transações, segurança entre outros.
Aplicação Swing
Aplicação JSF
chamada remota
Not
ifica
dor
(inte
rcep
tor)
Session Beans
(negócio)
Session Beans (DAO)
2424 www.mundoj.com.br
8
9
A decisão tomada acaba dando um peso maior para sua preo-cupação com a divisão de responsabilidades entre as classes e você decide tornar o Session Bean Notificador em um serviço de notificação conforme o padrão Application Service (ver quadro “Padrão Application Service”). Essa classe coordena e encapsula a invocação de serviços e classes de negócio que implementam a funcionalidade de cada notificação.
Quando você entra na sala do gerente, ele está trabalhando em seu
computador. Ele a princípio parece ignorar sua presença, mas em
seguida volta-se para você encarando-o com uma expressão bem
séria. Ele pede para você se sentar e sem fazer muitos rodeios diz
que o motivo de ter te chamado era para falar do seu desempenho
até o momento e falar sobre o seu futuro na empresa:
aqui na AvajOdnum achamos que o seu desempenho ficou muito abaixo de nossas expectativas. Todas as suas decisões para a implementação do mecanismo de notificação tive-ram consequências negativas para o nosso negócio. Muito trabalho braçal e demora no desenvolvimento, degradação
no desempenho, duplicação de código, problemas com ma-
nutenção. Infelizmente não consigo enxergar nenhum moti-
vo para manter você em nossa equipe. Gostaria de pedir que
você passasse no RH para acertar a sua situação.
Você fica bastante chateado por ter sido demitido, mas por outro
lado reconhece que nenhuma de suas decisões foi boa para a apli-
cação. Talvez você ainda não esteja preparado para assumir uma
função de arquiteto e ainda precise estudar bastante para chegar a
esse ponto. O mercado está cheio de oportunidades e resta a você
agora começar novamente em outra empresa procurando apren-
der com os erros que cometeu.
Como forma de testar esse novo passo do desenvolvimento
será implantada a primeira integração com o sistema de um dos
maiores clientes da AvajOdnum. O cliente disponibilizou um web
service em um dos seus sistemas para ser invocado na chamada
de algumas funcionalidades. Não se sabe exatamente qual fun-
cionalidade será executada pela aplicação dele. A princípio serão
utilizadas condicionais simples para definir quais chamadas de-
verão invocar o serviço do cliente, porém isso deve ser mudado
na última fase da implantação do mecanismo de notificações.
A funcionalidade de auditoria foi transferida para uma classe
auxiliar responsável por receber as informações do método
invocado e adicionar as informações em um arquivo. Uma classe
equivalente foi criada para encapsular a chamada do web service
do cliente. O Session Bean Notificador com o serviço de notifi-
cação seleciona a classe auxiliar que será chamada utilizando
condicionais no próprio código.
A criação da funcionalidade foi rapidamente feita por você
mesmo e em alguns testes realizados houve, como já era esperado, um pequeno aumento no tempo de resposta da aplicação considerado aceitável. Depois que a modificação foi para o servidor de produção, em alguns períodos do dia o serviço do cliente acaba ficando um pouco lento e demorando a responder, o que impactou diretamente no desempenho da aplicação percebido pelos usuários.
Outro ponto problemático na solução foi o gerenciamento de transações. Em casos que a notificação acontecia antes da execução da funcionalidade e o serviço era invocado, não havia como avisar o sistema do cliente do erro caso houvesse um rollback na transação, pois ele não é transacional. Houve um dia em que um processo foi disparado na aplicação do cliente a partir da chamada no web service e houve um rollback da transação depois. Nem precisa dizer que foi a maior confusão para consertar o problema.
Essa implementação concretizou um dos maiores receios da gerência que a inserção das notificações traria problemas para o desempenho da aplicação. Diversas reclamações dos clientes, tanto por causa da demora na execução das funcionalidades quanto pelo problema com a transação, aju-daram a aumentar a pressão na empresa. Esse já é a sua segunda decisão que traz consequências negativas para a aplicação e você começa a sentir que “sua batata está assando”. Com isso, você sabe que seu próximo passo será decisivo para sua carreira na empresa.
A próxima decisão a ser tomada irá influenciar a facilidade de adição de novas ações a serem acionadas pela notificação. No momento, estão implementadas as funcionalidades de auditoria e a chamada a aplicação de um dos clientes, porém você sabe que várias outras ações serão inseridas no futuro. Atualmente condicionais no próprio código verificam as condições de execução de cada tipo de notificação, porém você deve criar um mecanismo que facilite a adição desse tipo de funcionalidade. É preciso levar em consideração que mais de uma ação pode ser realizada a partir de uma notificação e que pode haver lógicas comuns em funcionalidades de notificação diferentes. Qual será a estratégia que você irá escolher para fazer isso?
que chama métodos abstratos para serem implementados por várias subclasses usando o padrão Template Method, vá para 14.
pelo tratamento das mensagens usando o padrão Chain of Responsibili-ty, vá para 20.
fim
Aplicação Swing
Aplicação JSF
chamada remota
Session Bean (Serviço de Notificação)
Classe Auxiliar
Classe Auxiliar
Session Beans
(negócio)
Session Beans (DAO)
2525
10A decisão tomada acaba dando um peso maior para sua preocupação com a divisão de responsabilidades entre as classes e você decide fazer o interceptor No-tificador invocar um EJB, que será utilizado como um serviço de notificação conforme o padrão Application Service (ver quadro “Padrão Application Service”). Essa classe coordena e encapsula a invocação de serviços e classes de negócio que implementam a funcionalidade de cada notificação.
Como forma de testar esse novo passo do desen-volvimento será implantada a primeira integração com o sistema de um dos maiores clientes da AvajOdnum. O cliente disponibilizou um web service em um dos seus sistemas para ser invo-cado na chamada de algumas funcionalidades. Não se sabe exatamente qual funcionalidade será executada pela aplicação dele. A princípio serão utilizadas condicionais simples para definir quais chamadas deverão invocar o serviço do cliente, porém isso deve ser mudado na última fase da implantação do mecanismo de notificações.
O interceptor recebe agora injeção de dependên-cia (ver quadro “Padrão Injeção de Dependência”) do EJB com o serviço de notificação e delega todas as chamadas para ele. A funcionalidade de auditoria foi transferida para uma classe auxiliar responsável por receber as informações do mé-todo invocado e adicionar as informações em um arquivo. Uma classe equivalente foi criada para encapsular a chamada do web service do cliente. O Session Bean com o serviço de notificação sele-ciona a classe auxiliar que será chamada utilizan-do condicionais no próprio código.
A criação da funcionalidade foi rapidamente feita por você mesmo
e em alguns testes realizados houve, como já era esperado, um
pequeno aumento no tempo de resposta da aplicação considerado
aceitável. Depois que a modificação foi para o servidor de produção,
em alguns períodos do dia o serviço do cliente acaba ficando um
pouco lento e demorando a responder, o que impactou diretamente
no desempenho da aplicação percebido pelos usuários.
Outro ponto problemático na solução foi o gerenciamento de tran-
sações. Em um caso que a notificação acontecia antes da execução
da funcionalidade e o serviço era invocado, não havia como avisar o
sistema do cliente do erro caso houvesse um rollback na transação,
pois ele não é transacional. Houve um dia em que um processo foi
disparado na aplicação do cliente a partir da chamada no web servi-
ce e houve um rollback da transação depois. Nem precisa dizer que
foi a maior confusão para consertar o problema.
Essa implementação concretizou um dos maiores receios da ge-
rência que a inserção das notificações traria problemas para o
desempenho da aplicação. Diversas reclamações dos clientes, tanto
por causa da demora na execução das funcionalidades quanto pelo
problema com a transação, ajudaram a aumentar a pressão na em-
presa. Apesar de você ter acertado na sua primeira decisão, essa
segunda causou certo desconforto no ambiente de trabalho. Você
percebe nesse momento que seu próximo passo pode ser decisivo
para sua carreira na empresa.
A próxima decisão a ser tomada irá influenciar a facilidade de adição de
novas ações a serem acionadas pela notificação. No momento, estão
implementadas as funcionalidades de auditoria e a chamada a aplicação
de um dos clientes, porém você sabe que várias outras ações serão in-
seridas no futuro. Atualmente condicionais no próprio código verificam
as condições de execução de cada tipo de notificação, porém você deve
criar um mecanismo que facilite a adição desse tipo de funcionalidade. É
preciso levar em consideração que mais de uma ação pode ser realizada
a partir de uma notificação e que pode haver lógicas comuns em funcio-
nalidades de notificação diferentes. Qual será a estratégia que você irá
escolher para fazer isso?
notificações que chama métodos abstratos para serem imple-
mentados por várias subclasses usando o padrão Template
Method, vá para 19.
-
ponsáveis pelo tratamento das mensagens usando o padrão
Chain of Responsibility, vá para 15.
Aplicação Swing
Aplicação JSF
chamada remota
Serviço de Notificação
(Session Bean)
Not
ifica
dor
(inte
rcep
tor)
Classe Auxiliar
Classe Auxiliar
Session Beans
(negócio)
Session Beans (DAO)
zan-
Meth
ponsávei
Chain of Res
2626 www.mundoj.com.br
11A sua decisão opta por utilizar a delegação como mecanismo para permitir a criação de novos proces-sadores de notificação. Apenas um Message-driven Bean é responsável por realizar o processamento das notificações. Com base nas informações das mensa-gens, o processador de notificações utiliza regras para definir quais processadores irão compor a cadeia de processamento. Ele é responsável por montar a cadeia de responsabilidades (ver quadro "Padrão Chain of Responsibility") que irá realizar de fato o processamen-to. Cada classe processadora é uma classe que realiza o seu processamento e em seguida delega a execução ao próximo processador da corrente.
A validação da solução agora será com a implemen-
tação das outras funcionalidades de notificação,
algumas de necessidade interna da empresa, como
a contabilização de acessos para fins de cobrança,
e outras por solicitações dos clientes, normalmente
para invocação de algum sistema deles. Existe um
grande número de solicitações e os desenvolvedo-
res irão começar a trabalhar na criação de cada uma
delas. Espera-se que a estrutura adotada consiga
fornecer flexibilidade para a inserção de diversos
processadores diferentes da forma mais simples
possível.
O primeiro passo foi refatorar a funcionalidade já
existente adaptando-a para a nova estrutura. A fun-
cionalidade de auditoria foi isolada em uma classe processadora as-sim como a invocação do web service do cliente. O Message-driven Bean também foi modificado para montar as cadeias de processa-mento com as classes processadoras. Por exemplo, o processamen-to de notificação de algumas funcionalidades era composto pelo registro de auditoria e a chamada do web service.
Quando os desenvolvedores começaram a desenvolver as outras funcionalidades, percebeu-se que entre diversos tipos de notifica-ção havia grande parte da lógica em comum. Exemplos envolviam a obtenção de credenciais para o acesso aos sistemas dos clientes, criptografia de dados, validação de formato, transformação de mensagens, registro de erros ocorridos, entre outros. Com a cadeia de responsabilidades, foi possível reaproveitar componentes já existentes facilmente para compor a cadeia de processamento.
Um problema dessa abordagem foi que a montagem das cadeias começou a gerar uma grande quantidade de código condicional no Message-driven Bean. A solução encontrada foi a criação de um descritor XML com a definição das cadeias de processamento e as regras de inclusão ou não de determinadas classes processadoras. Esse XML é lido pelo Message-driven Bean em sua inicialização, que já monta todas as cadeias de processamento. A inclusão do XML além de desacoplar as classes processadoras do Message-driven bean, tornou simples adicionar ou remover um passo do processa-mento, aumentando a flexibilidade do mecanismo implantado.
Como resultado final foram criadas diversas classes processadoras, algumas específicas de alguns clientes e outras mais gerais, que eram utilizadas para compor as cadeias de execução. Isso evitou a repetição de código dentre diferentes processamentos de noti-ficação e permitiu um rápido desenvolvimento de muitas funcio-nalidades. O XML criado para a definição das cadeias ajudou no seu gerenciamento, tornando simples a manutenção e adição de funcionalidades.
Apesar de ter cometido um engano na sua primeira decisão, você conseguiu recuperar a confiança dos desenvolvedores e da gerência com suas escolhas seguintes. Os desenvolvedores ficam satisfeitos em não precisarem realizar tarefas braçais e repetitivas para imple-mentação dos processadores de notificação como foi na primeira parte do projeto. A gerência parece estar satisfeita com o resultado e ter esquecido um pouco a decepção inicial, visto que consegue facilmente agregar novos mecanismos de notificação, satisfazendo os clientes (e podendo cobrar mais deles) de forma ágil.
Um dia, o gerente de TI da AvajOdnum te chama na sala dele para uma conversa... Vá para (6).
Aplicação Swing
Aplicação JSF
chamada remota
Cadeia de Processamento
Cria e invoca
Classe Processadora
Classe Processadora ...
Session Bean
(Notificador)
Session Beans
(negócio)
Session Beans (DAO)
Servidor de Mensagens
Processador de Notificações
(Message-driven Bean)
segue sfazendo
gil.
ama na sala dele para
2727
12
13
Quando você entra na sala do gerente, ele é todo sorriso e pede
rapidamente para você se sentar. Ele te trata como se fossem gran-
des amigos! Após fazer algumas perguntas sem muita importância
para quebrar o gelo, ele diz que o motivo de ter te chamado era
para falar do seu desempenho até o momento e falar sobre o seu
futuro na empresa:
ficou muito acima de nossas expectativas iniciais. Todas suas decisões na arquitetura foram acertadas e a funcionalidade ficou pronta muito antes do que nós esperávamos. Os de-senvolvedores todos comentam pelos cantos da empresa a respeito de como você é inteligente e saca muito de Java e de arquitetura.
Você balança a cabeça com ucerto orgulho, mas tentando de-
monstrar um pouco de humildade, e ele continua:
-
quirimos outra empresa e precisaremos integrar os nossos
sistemas com os sistemas deles. Para isso, estamos crian-
do a posição de arquiteto corporativo em nossa empresa
a qual queremos que você assuma. Outros arquitetos irão
ser responsáveis pelas aplicações individualmente, mas será
você quem terá uma visão geral das aplicações da empre-
sa e todas as decisões importantes devem passar por você.
Obviamente haverá uma compensação financeira por esse
aumento de responsabilidade.
A conversa segue por aproximadamente uma hora e ele comenta
mais detalhes sobre a expansão da empresa, indicações de pessoas
para assumirem as posições de arquiteto e o quanto o seu salário
irá aumentar. Você sai da conversa satisfeito e motivado para con-
tinuar o trabalho na área de arquitetura, porém reconhece que é
importante nunca parar de aprender!
A sua decisão opta por utilizar a herança como mecanis-mo para permitir a criação de novos processadores de notificação. Para a solução, criou-se uma classe abstrata que possui no método onMessage() um algoritmo ge-nérico para processamento das mensagens, que chama métodos abstratos que devem ser implementados pelas subclasses conforme o padrão Template Method (ver o quadro “Padrão Template Method”). Para o container, cada subclasse que implementa o processador abstrato é considerado um Message-driven Bean diferente e precisa ser considerado individualmente. O interceptor Notificador irá setar nas mensagens algumas proprieda-des, como a funcionalidade e o cliente responsável por aquela invocação, que irão permitir que cada Message-driven Bean filtre apenas as mensagens que são do seu interesse.
fim
A validação da solução agora será com a implementação
das outras funcionalidades de notificação, algumas de
necessidade interna da empresa, como a contabilização
de acessos para fins de cobrança, e outras por solicitações dos clientes,
normalmente para invocação de algum sistema deles. Existe um grande
número de solicitações e os desenvolvedores irão começar a trabalhar na
criação de cada uma delas. Espera-se que a estrutura adotada consiga
fornecer flexibilidade para a inserção de diversos processadores diferen-
tes da forma mais simples possível.
O primeiro passo foi refatorar a funcionalidade já existente adaptando-
a para a nova estrutura. O interceptor foi modificado para configurar
propriedades nas mensagens para permitir que cada Message-driven
Bean possa filtrar apenas as que deve tratar. O processador abstrato im-
plementou o método onMessage() com algum processamento comum e
chamando alguns métodos abstratos. A parte de auditoria foi colocada
em um bean sem filtro e a chamada ao web service do cliente em um
bean que filtrava somente as mensagens relativas àquele cliente. Para
essas duas funcionalidades, a solução funcionou bem.
Quando os desenvolvedores começaram a desenvolver as outras fun-
cionalidades, percebeu-se que entre diversos tipos de notificação havia
grande parte da lógica em comum. Exemplos envolviam a obtenção
de credenciais para o acesso aos sistemas dos clientes, criptografia de
dados, validação de formato, transformação de mensagens, registro de
erros ocorridos, entre outros. A princípio tentou-se repetir a estratégia
do padrão Template Method, criando superclasses que implementavam
essas funcionalidades e chamavam métodos abstratos que seriam imple-
mentados nas subclasses. Foi criado, por exemplo, uma classe abstrata
que incorporava o registro de erros e outra que incorporava a busca das
credenciais de acesso. Houve um momento em que um desenvolvedor
disse que o processador de notificação que estava criando precisava das
duas funcionalidades e perguntou qual das duas superclasses ele deveria
estender. Neste momento, você se lembrou de uma frase que leu no livro
Implementation Patterns de Kent Beck: a herança é uma carta que você
só pode jogar uma vez.
Aplicação Swing
Aplicação JSF
chamada remota
Servidor de Mensagens
Processadores Concretos (Message-driven Bean)
Processador Abstrato
implementarem métodos abstratos
Not
ifica
dor
(inte
rcep
tor)
Session Beans
(negócio)
Session Beans (DAO)
2828 www.mundoj.com.br
O resultado final foi que a profundidade de herança dos processadores
ficou muito grande, com a presença de bastante código duplicado e
hierarquias de classe paralelas. Existiam também muitos Message-driven
Beans consumindo mensagens na mesma fila. Apesar disso ainda não ter
se tornado um problema, um arquiteto de outro projeto afirmou que isso
não é bom para escalabilidade, visto que fica difícil ajustar a quantidade
de beans que irá processar notificações em paralelo no container. Com
essa estrutura, por exemplo, é difícil saber quais os notificadores que
tratam uma determinada mensagem.
Devido ao seu resultado inicial promissor, existia uma grande expectativa
que você conseguiria finalizar a solução provento uma forma simples e
fácil de adicionar novos processadores. Devido ao grande número de
classes, da duplicação de código e da profundidade da hierarquia, o
código dos processadores é difícil de ser criado, mantido e gerenciado. A
gerência se decepciona, pois isso gera uma dificuldade adicional na inte-
gração com os sistemas dos clientes, o que é um ponto muito importante
no plano estratégico da empresa para conquistar novos clientes e manter
os clientes antigos.
Um dia, o gerente de TI da AvajOdnum te chama na sala dele para uma
conversa... Vá para (6).
14A sua decisão opta por utilizar a herança como meca-nismo para permitir a criação de novos processadores de notificação. Para a solução, criou-se uma classe abstrata que possui um método para o processamento das notificações com um algoritmo genérico, que cha-ma métodos abstratos que devem ser implementados pelas subclasses conforme o padrão Template Method (ver o quadro “Padrão Template Method”). O serviço de notificação é o responsável por identificar qual a subclasse que deverá processar cada notificação rece-bida e utiliza as informações do usuário e da invocação (session bean, método, parâmetros) para fazer isso.
A validação da solução agora será com a implementa-
ção das outras funcionalidades de notificação, algumas
de necessidade interna da empresa, como a contabi-
lização de acessos para fins de cobrança, e outras por
solicitações dos clientes, normalmente para invocação
de algum sistema deles. Existe um grande número de
solicitações e os desenvolvedores irão começar a tra-
balhar na criação de cada uma delas. Espera-se que a
estrutura adotada consiga fornecer flexibilidade para a
inserção de diversos processadores diferentes da forma
mais simples possível.
O primeiro passo foi refatorar a funcionalidade já exis-
tente adaptando-a para a nova estrutura. O processador
abstrato implementou o método processarNotificacao() com algum pro-cessamento comum e chamando alguns métodos abstratos. A parte de auditoria foi colocada em uma subclasse do processador abstrato. Como todas as funcionalidades que precisavam da chamada do web service também precisavam do registro de auditoria, esse processador estendeu a classe que fazia auditoria. Para essas duas funcionalidades, a solução funcionou bem.
Quando os desenvolvedores começaram a desenvolver as outras fun-cionalidades, percebeu-se que entre diversos tipos de notificação havia grande parte da lógica em comum. Exemplos envolviam a obtenção de credenciais para o acesso aos sistemas dos clientes, criptografia de dados, validação de formato, transformação de mensagens, registro de erros ocorridos, entre outros. A princípio tentou-se repetir a estratégia do pa-drão Template Method, criando superclasses que implementavam essas funcionalidades e chamavam métodos abstratos que seriam implemen-tados nas subclasses. Foram criadas, por exemplo, uma classe abstrata que incorporava o registro de erros e outra que incorporava a busca das credenciais de acesso. Houve um momento em que um desenvolvedor disse que o processador de notificação que estava criando precisava das duas funcionalidades e perguntou qual das duas superclasses ele deveria estender. Neste momento, você se lembrou de uma frase que leu no livro Implementation Patterns de Kent Beck: a herança é uma carta que você só pode jogar uma vez.
Devido a lógica de identificação da classe de processamento no session bean ter ficado muito complicada, foi criado um arquivo XML para o ma-peamento das chamadas para o processador. A profundidade de herança dos processadores ficou muito grande, com a presença de muito código duplicado e hierarquias de classe paralelas. Existiam tantas particularida-des que era raro uma classe de processamento ser utilizada para mais de um tipo de notificação.
Suas escolhas foram um desastre desde o começo. Muito trabalho braçal, degradação do desempenho, dificuldade de manutenção. Todos esses indesejáveis fatores fizeram com que os desenvolvedores ficassem muito desmotivados em trabalhar nesse projeto. A gerência fica descontente ao notar que existe uma dificuldade generalizada na integração do mecanis-mo com os sistemas dos clientes.
Um dia, o gerente de TI da AvajOdnum te chama na sala dele para uma conversa... Vá para (9).
Aplicação Swing
Aplicação JSF
chamada remota
Session Bean (Serviço de Notificação)
implementarem métodos abstratos
Session Beans
(negócio)
Session Beans (DAO)
Processador Abstrato
Processador Concreto (POJO)
2929
15A sua decisão opta por utilizar a delegação como mecanismo para permitir a criação de novos proces-sadores de notificação. Com base nas informações das mensagens, o serviço de notificação utiliza regras para definir quais processadores irão compor a cadeia de processamento. Ele é responsável por montar a cadeia de responsabilidades (ver quadro "Padrão Chain of Responsibility") que irá realizar de fato o processamen-to. Cada classe processadora é uma classe que realiza o seu processamento e em seguida delega a execução ao próximo processador da corrente.
A validação da solução agora será com a implemen-tação das outras funcionalidades de notificação, algumas de necessidade interna da empresa, como a contabilização de acessos para fins de cobrança, e ou-tras por solicitações dos clientes, normalmente para invocação de algum sistema deles. Existe um grande número de solicitações e os desenvolvedores irão começar a trabalhar na criação de cada uma delas. Espera-se que a estrutura adotada consiga fornecer flexibilidade para a inserção de diversos processado-res diferentes da forma mais simples possível.
O primeiro passo foi refatorar a funcionalidade já existente adaptando-a para a nova estrutura. A fun-cionalidade de auditoria foi isolada em uma classe processadora assim como a invocação do web service do cliente. O Session Bean com o serviço de notifica-ção também foi modificado para montar as cadeias de processamento com as classes processadoras. Por
exemplo, o processamento de notificação de algumas funcionalidades
era composto pelo registro de auditoria e a chamada do web service.
Quando os desenvolvedores começaram a desenvolver as outras fun-
cionalidades, percebeu-se que entre diversos tipos de notificação havia
grande parte da lógica em comum. Exemplos envolviam a obtenção
de credenciais para o acesso aos sistemas dos clientes, criptografia de
dados, validação de formato, transformação de mensagens, registro de
erros ocorridos, entre outros. Com a cadeia de responsabilidades, foi
possível reaproveitar componentes já existentes facilmente para compor
a cadeia de processamento.
Um problema dessa abordagem foi que a montagem das cadeias come-
çou a gerar uma grande quantidade de código condicional no Session
Bean. A solução encontrada foi a criação de um descritor XML com a de-
finição das cadeias de processamento e as regras de inclusão ou não de
determinadas classes processadoras. Esse XML é lido pelo Session Bean
com o serviço de notificação em sua inicialização, que já monta todas
as cadeias de processamento. A inclusão do XML além de desacoplar as
classes processadoras do serviço de notificação, tornou simples adicionar
ou remover um passo do processamento, aumentando a flexibilidade do
mecanismo implantado.
Como resultado final, foram criadas diversas classes processadoras,
algumas específicas de alguns clientes e outras mais gerais, que eram
utilizadas para compor as cadeias de execução. Isso evitou a repetição de
código dentre diferentes processamentos de notificação e permitiu um
rápido desenvolvimento de muitas funcionalidades. O XML criado para a
definição das cadeias ajudou no seu gerenciamento, tornando simples a
manutenção e adição de funcionalidades.
Apesar do sistema de notificação ter degradado o desempenho da
aplicação, você conseguiu fazer com que ele pudesse ser facilmente
acoplado no sistema existente e que novos processadores pudessem ser
facilmente introduzidos. Os desenvolvedores ficam satisfeitos em não
precisarem realizar tarefas braçais e repetitivas em nenhuma fase do
projeto, tendo consciência que estavam trabalhando em cima de uma
boa estrutura. A gerência parece estar satisfeita com o resultado e ter
esquecido um pouco da demora nas requisições, visto que consegue
facilmente agregar novos mecanismos de notificação, satisfazendo os
clientes (e podendo cobrar mais deles) de forma ágil. Quem sabe com
essa boa estrutura você não consegue refatorar alguma coisa para resol-
ver esse problema do desempenho.
Um dia, o gerente de TI da AvajOdnum te chama na sala dele para uma
conversa... Vá para (6).
Aplicação Swing
Aplicação JSF
chamada remota
Cadeia de Processamento
Cria e invoca
Serviço de Notificação
(Session Bean)
Classe Processadora
Classe Processadora ...
Not
ifica
dor
(inte
rcep
tor)
Session Beans
(negócio)
Session Beans (DAO)
3030 www.mundoj.com.br
16
17
A sua decisão opta por utilizar a herança como meca-nismo para permitir a criação de novos processadores de notificação. Para a solução, criou-se uma classe abs-trata que possui no método onMessage() um algorit-mo genérico para processamento das mensagens, que chama métodos abstratos que devem ser implementa-dos pelas subclasses conforme o padrão Template Me-thod (ver o quadro “Padrão Template Method”). Para o container, cada subclasse que implementa o processa-dor abstrato é considerado um Message-driven Bean diferente e precisa ser considerado individualmente. O interceptor Notificador irá setar nas mensagens algu-mas propriedades, como a funcionalidade e o cliente responsável por aquela invocação, que irão permitir que cada Message-driven Bean filtre apenas as mensa-gens que são do seu interesse.
A sua decisão opta por utilizar a delegação como meca-nismo para permitir a criação de novos processadores de notificação. Apenas um Message-driven Bean é responsável por realizar o processamento das notificações. Com base nas informações das mensagens, o processador de notificações utiliza regras para definir quais processadores irão compor
a cadeia de processamento. Ele é responsável por montar a cadeia de responsabilidades (ver quadro "Padrão Chain of Responsibility") que irá realizar de fato o processamento. Cada classe processadora é uma classe que realiza o seu processamento e em seguida delega a execução ao próximo processador da corrente.
A validação da solução agora será com a implementação das outras funcionalidades de notificação, algumas de necessidade interna da empresa, como a contabilização de acessos para fins de cobrança, e outras por solicita-ções dos clientes, normalmente para invocação de algum sistema deles. Existe um grande número de solicitações e os desenvolvedores irão começar a trabalhar na criação de cada uma delas. Espera-se que a estrutura adotada consiga fornecer flexibilidade para a inserção de diversos processadores diferentes da forma mais simples possível.
O primeiro passo foi refatorar a funcionalidade já exis-tente adaptando-a para a nova estrutura. O interceptor foi modificado para configurar propriedades nas mensa-gens para permitir que cada Message-driven Bean possa filtrar apenas as que deve tratar. O processador abstrato
implementou o método onMessage() com algum processamento comum
e chamando alguns métodos abstratos. A parte de auditoria foi colocada
em um bean sem filtro e a chamada ao web service do cliente em um bean
que filtrava somente as mensagens relativas àquele cliente. Para essas
duas funcionalidades, a solução funcionou bem.
Quando os desenvolvedores começaram a desenvolver as outras funciona-
lidades, percebeu-se que entre diversos tipos de notificação havia grande
parte da lógica em comum. Exemplos envolviam a obtenção de credenciais
para o acesso aos sistemas dos clientes, criptografia de dados, validação de
formato, transformação de mensagens, registro de erros ocorridos, entre
outros. A princípio, tentou-se repetir a estratégia do padrão Template Me-
thod, criando superclasses que implementavam essas funcionalidades e
chamavam métodos abstratos que seriam implementados nas subclasses.
Foi criado, por exemplo, uma classe abstrata que incorporava o registro de
erros e outra que incorporava a busca das credenciais de acesso. Houve um
momento em que um desenvolvedor disse que o processador de notifica-
ção que estava criando precisava das duas funcionalidades e perguntou
qual das duas superclasses ele deveria estender. Neste momento, você se
lembrou de uma frase que leu no livro Implementation Patterns de Kent
Beck: a herança é uma carta que você só pode jogar uma vez.
O resultado final foi que a profundidade de herança dos processadores
ficou muito grande, com a presença de bastante código duplicado e
hierarquias de classe paralelas. Existiam também muitos Message-driven
Beans consumindo mensagens na mesma fila. Apesar disso ainda não ter
se tornado um problema, um arquiteto de outro projeto afirmou que isso
não é bom para escalabilidade, visto que fica difícil ajustar a quantidade de
beans que irá processar notificações em paralelo no container. Com essa
estrutura, por exemplo, é difícil de saber quais os notificadores que tratam
uma determinada mensagem.
Você havia se recuperado de seu primeiro erro com a utilização das
chamadas assíncronas para não permitir a degradação do desempenho
com as notificações, porém essa última decisão foi uma decepção para
todos. Devido ao grande número de classes, da duplicação de código e
da profundidade da hierarquia, o código dos processadores é difícil de ser
criado, mantido e gerenciado. A gerência nota que existe uma dificuldade
adicional na integração com os sistemas dos clientes, o que é um ponto
muito importante no plano estratégico da empresa para conquistar novos
clientes e manter os clientes antigos.
Um dia, o gerente de TI da AvajOdnum te chama na sala dele para uma
conversa... Vá para (18).
Aplicação Swing
Aplicação JSF
chamada remota
Session Bean (Notificador)
Session Beans
(negócio)
Session Beans (DAO)
Servidor de Mensagens
Processador Abstrato
implementam métodos abstratos
Processadores Concretos
(Message-driven Bean)
3131
A validação da solução agora será com a implementação das outras fun-
cionalidades de notificação, algumas de necessidade interna da empresa,
como a contabilização de acessos para fins de cobrança, e outras por
solicitações dos clientes, normalmente para invocação de algum sistema
deles. Existe um grande número de solicitações e os desenvolvedores irão
começar a trabalhar na criação de cada uma delas. Espera-se que a es-
trutura adotada consiga fornecer flexibilidade para a inserção de diversos
processadores diferentes da forma mais simples possível.
O primeiro passo foi refatorar a funcionalidade já existente adaptando-a
para a nova estrutura. A funcionalidade de auditoria foi isolada em uma
classe processadora assim como a invocação do web service do cliente.
O Message-driven Bean também foi modificado para montar as cadeias
de processamento com as classes processadoras. Por exemplo, o proces-
samento de notificação de algumas funcionalidades era composto pelo
registro de auditoria e a chamada do web service.
Quando os desenvolvedores começaram a desenvolver as outras fun-
cionalidades, percebeu-se que entre diversos tipos de notificação havia
grande parte da lógica em comum. Exemplos envolviam a obtenção
de credenciais para o acesso aos sistemas dos clientes, criptografia de
dados, validação de formato, transformação de mensagens, registro de
erros ocorridos, entre outros. Com a cadeia de responsabilidades foi
possível reaproveitar componentes já existentes facilmente para compor
a cadeia de processamento.
Um problema dessa abordagem foi que a montagem das cadeias come-
çou a gerar uma grande quantidade de código condicional no Message-
driven Bean. A solução encontrada foi a criação de um descritor XML
com a definição das cadeias de processamento e as regras de inclusão
ou não de determinadas classes processadoras. Esse XML é lido pelo
Message-driven Bean em sua inicialização, que já monta todas as cadeias
de processamento. A inclusão do XML além de desacoplar as classes
processadoras do Message-driven bean, tornou simples adicionar ou
remover um passo do processamento, aumentando a flexibilidade do
mecanismo implantado.
Como resultado final, foram criadas diversas classes processadoras,
algumas específicas de alguns clientes e outras mais gerais, que eram
utilizadas para compor as cadeias de execução. Isso evitou a repetição de
código dentre diferentes processamentos de notificação e permitiu um
rápido desenvolvimento de muitas funcionalidades. O XML criado para a
definição das cadeias ajudou no seu gerenciamento, tornando simples a
manutenção e adição de funcionalidades.
Ao olhar para o resultado final do mecanismo de notificação você se
orgulha de suas decisões e fica com uma sensação de dever cumprido.
Os desenvolvedores ficam satisfeitos por estarem trabalhando em pro-
jeto em que podem se focar no desenvolvimento de funcionalidades
que agregam valor ao projeto sem precisarem realizar tarefas braçais e
repetitivas. A gerência parece estar satisfeita com o resultado, visto que
consegue facilmente agregar novos mecanismos de notificação, satisfa-
zendo os clientes (e podendo cobrar mais deles) de forma ágil.
Um dia, o gerente de TI da AvajOdnum te chama na sala dele para uma
conversa... Vá para (12).
18Quando você entra na sala do gerente, ele está trabalhando em
seu computador. Ele a princípio parece ignorar sua presença,
mas em seguida volta-se para você encarando-o com uma
expressão bem séria. Ele pede para você se sentar e sem fazer
muitos rodeios diz que o motivo de ter te chamado era para falar
do seu desempenho até o momento e falar sobre o seu futuro
na empresa:
-nho ficou abaixo de nossas expectativas iniciais. Apesar de você ter acertado em algumas escolhas para a im-plementação do mecanismo de notificação de um de nossos sistemas, você mesmo sabe que a maioria delas poderiam ser melhores. Várias de suas decisões tornaram complexa a edição de novas funcionalidades no sistema.
Você balança a cabeça admitindo que errou em vários pontos
e ele continua:
-
mentação você demonstrou conhecimento em diversas
tecnologias e que é eficiente na criação de código. Apesar
de acharmos que você ainda não tem experiência para se
tornar um arquiteto, se você se interessar gostaríamos
que continuasse fazendo parte da equipe como desen-
volvedor. É claro que vai ganhar um pouco menos, porém
acredito que terá várias oportunidades para aprender.
Você fica um pouco chateado por não ter dado conta de cum-
prir de forma satisfatória com a função de arquiteto que havia
lhe sido dada. Por outro lado, você sente certo alívio por ainda
ter um emprego. Em situações como esta é que é preciso ter
humildade, reconhecer suas limitações e estudar bastante para
chegar no ponto em que se almeja. Quem sabe daqui a algum
tempo você não consegue recuperar a posição que perdeu. Você
escuta conversas de corredor que a empresa está se expandindo
e oportunidades não irão faltar.
fim
Aplicação Swing
Aplicação JSF
chamada remota
Cadeia de Processamento
Classe Processadora
Classe Processadora ...
Session Beans
(negócio)
Session Beans (DAO)
Servidor de Mensagens
Processador de Notificações
(Message-driven Bean)
cria e invoca
Not
ifica
dor
(inte
rcep
tor)
3232 www.mundoj.com.br
19
20
A sua decisão opta por utilizar a herança como meca-nismo para permitir a criação de novos processadores de notificação. Para a solução, criou-se uma classe abstrata que possui um método para o processamento das notificações com um algoritmo genérico, que cha-ma métodos abstratos que devem ser implementados pelas subclasses conforme o padrão Template Method (ver o quadro “Padrão Template Method”). O serviço de notificação é o responsável por identificar qual a subclasse que deverá processar cada notificação rece-bida e utiliza as informações do usuário e da invocação (session bean, método, parâmetros) para fazer isso.
A sua decisão opta por utilizar a delegação como meca-
nismo para permitir a criação de novos processadores de
notificação. Com base nas informações das mensagens, o
serviço de notificação utiliza regras para definir quais pro-
cessadores irão compor a cadeia de processamento. Ele é
responsável por montar a cadeia de responsabilidades (ver
quadro "Padrão Chain of Responsibility") que irá realizar
de fato o processamento. Cada classe processadora é uma
classe que realiza o seu processamento e em seguida delega
a execução ao próximo processador da corrente.
A validação da solução agora será com a implementação das outras funcionalidades de notificação, algumas de necessidade interna da empresa, como a contabilização de acessos para fins de cobrança, e outras por solici-tações dos clientes, normalmente para invocação de algum sistema deles. Existe um grande número de soli-citações e os desenvolvedores irão começar a trabalhar na criação de cada uma delas. Espera-se que a estrutura adotada consiga fornecer flexibilidade para a inserção de diversos processadores diferentes da forma mais simples possível.
O primeiro passo foi refatorar a funcionalidade já exis-tente adaptando-a para a nova estrutura. O interceptor não precisou ser modificado, pois já enviava as informa-ções necessárias para o serviço de notificação escolher a classe que irá fazer o processamento. O processador
abstrato implementou o método processarNotificacao() com algum pro-cessamento comum e chamando alguns métodos abstratos. A parte de auditoria foi colocada em uma subclasse do processador abstrato. Como todas as funcionalidades que precisavam da chamada do web service também precisavam do registro de auditoria, esse processador estendeu a classe que fazia auditoria. Para essas duas funcionalidades, a solução funcionou bem.
Quando os desenvolvedores começaram a desenvolver as outras fun-cionalidades, percebeu-se que entre diversos tipos de notificação havia grande parte da lógica em comum. Exemplos envolviam a obtenção de credenciais para o acesso aos sistemas dos clientes, criptografia de dados, validação de formato, transformação de mensagens, registro de erros ocorridos, entre outros. A princípio, tentou-se repetir a estratégia do padrão Template Method, criando superclasses que implementavam essas funcionalidades e chamavam métodos abstratos que seriam imple-mentados nas subclasses. Foi criado, por exemplo, uma classe abstrata que incorporava o registro de erros e outra que incorporava a busca das credenciais de acesso. Houve um momento em que um desenvolvedor disse que o processador de notificação que estava criando precisava das duas funcionalidades e perguntou qual das duas superclasses ele deveria estender. Neste momento, você se lembrou de uma frase que leu no livro Implementation Patterns de Kent Beck: a herança é uma carta que você só pode jogar uma vez.
Devido a lógica de identificação da classe de processamento no session bean ter ficado muito complicada, foi criado um arquivo XML para o mapeamento das chamadas para o processador. A profundidade de herança dos processadores ficou muito grande, com a presença de bastante código duplicado e hierarquias de classe paralelas. Existiam tantas particularidades que era muito raro uma classe de processamento ser utilizada para mais de um tipo de notificação.
Você havia começado bem com a escolha do uso dos interceptors, porém as duas últimas decisões foram uma decepção para todos. Devido ao grande número de classes, da duplicação de código e da profundidade da hierarquia, o código dos processadores é difícil de ser criado, mantido e gerenciado. A gerência nota que existe uma dificuldade adicional na integração com os sistemas dos clientes, o que é um ponto muito impor-tante no plano estratégico da empresa para conquistar novos clientes e manter os clientes antigos.
Um dia, o gerente de TI da AvajOdnum te chama na sala dele para uma conversa... Vá para (18).
Aplicação Swing
Aplicação JSF
chamada remota
Serviço de Notificação
(Session Bean)
Not
ifica
dor
(inte
rcep
tor)
Processador Abstrato
Session Beans (negócio)
Session Beans (DAO)
implementam métodos abstratos
Processador Concreto
(POJO)
A validação da solução agora será com a implementação das outras fun-
cionalidades de notificação, algumas de necessidade interna da empresa,
como a contabilização de acessos para fins de cobrança, e outras por
solicitações dos clientes, normalmente para invocação de algum sistema
deles. Existe um grande número de solicitações e os desenvolvedores irão
começar a trabalhar na criação de cada uma delas. Espera-se que a es-
trutura adotada consiga fornecer flexibilidade para a inserção de diversos
processadores diferentes da forma mais simples possível.
O primeiro passo foi refatorar a funcionalidade já existente adaptando-a
para a nova estrutura. A funcionalidade de auditoria foi isolada em uma
classe processadora assim como a invocação do web service do cliente.
O Session Bean com o serviço de notificação também foi modificado para
montar as cadeias de processamento com as classes processadoras. Por
exemplo, o processamento de notificação de algumas funcionalidades era
composto pelo registro de auditoria e a chamada do web service.
Quando os desenvolvedores começaram a desenvolver as outras fun-
cionalidades, percebeu-se que entre diversos tipos de notificação havia
grande parte da lógica em comum. Exemplos envolviam a obtenção
de credenciais para o acesso aos sistemas dos clientes, criptografia de dados, validação de formato, transformação de mensagens, registro de erros ocorridos, entre outros. Com a cadeia de responsabilidades foi possível reaproveitar componentes já existentes facilmente para compor a cadeia de processamento.
Um problema dessa abordagem foi que a montagem das cadeias come-çou a gerar uma grande quantidade de código condicional no Session Bean. A solução encontrada foi a criação de um descritor XML com a de-finição das cadeias de processamento e as regras de inclusão ou não de determinadas classes processadoras. Esse XML é lido pelo Session Bean com o serviço de notificação em sua inicialização, que já monta todas as cadeias de processamento. A inclusão do XML além de desacoplar as classes processadoras do serviço de notificação, tornou simples adicionar ou remover um passo do processamento, aumentando a flexibilidade do mecanismo implantado.
Como resultado final foram criadas diversas classes processadoras, algumas específicas de alguns clientes e outras mais gerais, que eram utilizadas para compor as cadeias de execução. Isso evitou a repetição de código dentre diferentes processamentos de notificação e permitiu um rápido desenvolvimento de muitas funcionalidades. O XML criado para a definição das cadeias ajudou no seu gerenciamento, tornando simples a manutenção e adição de funcionalidades.
Os problemas provenientes das suas duas primeiras decisões fizeram que sua credibilidade como arquiteto ficasse em baixa, porém essa última escolha levantou sua moral com a equipe e com a gerência. Os desen-volvedores ficam satisfeitos em não precisarem realizar tarefas braçais e repetitivas na inserção dos processadores. A gerência parece estar satis-feita com o resultado dessa fase, visto que consegue facilmente agregar novos mecanismos de notificação, satisfazendo os clientes (e podendo cobrar mais deles) de forma ágil.
Um dia, o gerente de TI da AvajOdnum te chama na sala dele para uma conversa... Vá para (18).
Este padrão foi proposto por Martin Fowler e refere-se ao processo de uma classe permitir que suas dependências possam ser defini-das, ou “injetadas”, externamente ao mesmo. Esse padrão também é conhecido como inversão de controle (IoC), já que o controle do processo de obter a dependência e compor o objeto dependente é deslocado a outro componente (como um container leve, por exemplo).
Um objeto precisa frequentemente da colaboração de outro objeto para realizar uma determinada tarefa. Em tese, o objeto que precisa da dependência precisaria somente de uma referência ao outro objeto, mas normalmente, ele acaba precisando também controlar seu ciclo de vida. A injeção de dependência transfere o processo de criar a dependência (com suas possíveis dependências) e injetá-la
em outro objeto, para que este possa contar com sua colaboração sem precisar controlar como esta é instanciada e inicializada.
Na prática, não é necessário utilizar especificamente um framework para aplicar a injeção de dependência. Somente o fato do processo de composição de um objeto e de suas dependências ser transferido para outro componente, já diminui o acoplamento entre o depen-dente e a dependência. A flexibilidade que é ganha com esse padrão vem do fato que uma classe pode depender de uma interface e qual-quer classe que a implementa pode ser injetada como dependência. Como a criação da instância não está amarrada internamente na classe, é possível facilmente alterar a dependência.
São três os elementos que compõem este padrão: um dependente,
O padrão injeção de dependência
33
Aplicação Swing
Aplicação JSF
chamada remota
Cadeia de Processamento
Classe Processadora
Classe Processadora ...
Session Beans (negócio)
Session Beans (DAO)
cria e invoca
Session Bean (Serviço de Notificação)
as dependências e o injetor. O dependente é o objeto que precisa da colaboração de outro objeto (a dependência) para realizar sua tarefa. O injetor é responsável por fornecer a dependência e compor o objeto dependente, para que este seja capaz de realizar sua tarefa. O injetor também é responsável pelo ciclo de vida tanto da depen-dência quanto do objeto dependente.
Existem três formas de injeção de dependência: por construtor, por setter e por interface. Seguem as descrições:
o objeto dependente possui um construtor que espera o tipo (preferencialmente uma interface) da dependên-cia. O injetor então instancia a dependência e instancia tam-bém o dependente, passando a dependência através do cons-trutor. O injetor pode ser implementado de várias formas, por exemplo, utilizando os padrões Abstract Factory ou Factory Method (padrões GoF). A vantagem de se ter um construtor que espera o tipo da dependência é que o objeto dependente, ao ser construído, está pronto para ser utilizado. Além disso, pelo construtor, pode-se perceber que é impossível utilizar o objeto dependente sem sua dependência.
a dependência chega para o dependente através de um método set que espera o tipo da dependência, sem a ne-cessidade de um construtor. Depois do objeto dependente já criado, o injetor instancia a dependência e a injeta no depen-dente através de um método set.
cria-se uma interface, e nela, define-se um método
que receberá a dependência. As classes que implementam a
interface possuem a implementação concreta do método que
recebe a dependência. A tarefa do injetor pode ser executada
tanto por um framework, que suporte injeção de dependência,
quanto por qualquer outro código que seja responsável pelo
arranjo destes objetos.
A especificação do EJB 3 suporta o padrão de injeção de dependên-
cia. Neste caso, o injetor é o próprio EJB container que é responsável
por, além de controlar o ciclo de vida dos EJBs, setar as dependências
relacionadas, como, por exemplo, fábricas de conexão com bancos
de dados, instâncias do EntityManager (para utilização de JPA) e até
mesmo referências a outros EJBs.
São utilizadas anotações nos atributos dos EJBs para que os mesmos
possam receber a injeção de dependência do container. O sistema
é similar ao uso de métodos setters, porém o injetor acessa dire-
tamente os atributos da classe. Exemplos dessas anotações são @
Resource, @EJB, @WebServiceRef e @PersistentContext.
Alguns frameworks, como o Spring e o Google Guice, oferecem
diversas outras opções para a utilização da injeção de dependência.
O padrão Decorator (GoF) permite que se acrescente funcionalidades de uma forma plugável em uma classe. Neste padrão, é criada uma classe que “decora” a classe original (chamada de decorator), acrescentando a execução de uma funcionalidade e em seguida delegando a execução à classe original. O decorator deve implementar a mesma interface da classe original, para que esse processo seja transparente para seus clientes. Para aplicação desse padrão, deve-se possuir controle sobre o processo de criação da classe decorada, para que o decorator possa ser acrescentado e retornado.
O processo de criação dos EJBs é controlado pelo container e, até a versão 2.1 da especificação, não era possível plugar novas funcionalidades a esse tipo de componente. Com o EJB 3, tornou-se possível criar classes chamadas de Inter-ceptors que podem interceptar a execução de uma método, possibilitando a adição de funcionalidades antes e depois de sua execução.
Os interceptadores do EJB 3 são objetos que são automaticamente chamados quando um método EJB é invocado e tem acesso a todos os seus dados, como parâmetros, retorno, exceções lançadas etc. Os interceptadores podem ser aplicados tanto aos Session Beans quanto aos Message-driven Beans. Eles po-dem ser aplicados em três níveis: default (para todos os beans de um módulo EJB), classe (para todos os métodos de um bean) e método (para somente um método específico). Ao definir interceptadores nos três níveis, os default são invocados primeiro, seguidos pelos de nível de classe e de método.
Um método interceptador pode ser definido tanto no próprio bean quanto
em uma classe separada, porém normalmente é mais recomendado mantê-lo separado dos métodos de negócio por questões de coesão. Para a definição de um interceptador, deve-se definir um (somente um) método com a anotação @AroundInvoke, que será chamado antes do método a ser interceptado. O método de interceptação recebe como parâmetro uma instância da interface InvocationContext e deve chamar o seu método proceed() quando desejar que o fluxo de execução siga para o próximo interceptor ou para classe de negócios se ele for o último.
Para indicar que um método deve ser interceptado, deve-se anotá-lo com @Interceptors, indicando a classe (ou as classes) que contém o método anotado com a anotação @AroundInvoke. Os interceptadores também podem ser definidos no descritor de distribuição, sendo que essa é a única opção para os interceptadores default. Pode-se indicar mais de um interceptador para uma classe ou método, e estes serão executados na ordem em que aparecerem na anotação ou no descritor de distribuição.
É possível também, por exemplo, indicar que um método não deve ser inter-ceptado pelos interceptadores default. Neste caso, anota-se o método com a anotação @ExcludeDefaultInterceptors. Da mesma forma, é possível indicar também que um método não deve ser interceptado pelos interceptadores de nível de classe, anotando-o com @ExcludeClassInterceptors. Em ambos os ca-sos, é possível indicar estas configurações através do descritor de distribuição.
EJB 3 Interceptors
3434 www.mundoj.com.br
Este padrão (que faz parte dos Core J2EE Patterns) propõe como solução
chamar serviços de modo assíncrono. Em aplicações empresariais, é mui-
to comum ter uma grande parte do processamento sendo feita de modo
síncrono. Em alguns casos, o processamento pode ser grande, inclusive
sendo realizado por várias aplicações que talvez nem estejam no mesmo
ambiente. Dessa forma, o processamento pode consumir tempo e recur-
sos consideráveis do servidor. Para esse tipo de processamento, pode-se
utilizar um Service Activator, que é um serviço que recebe solicitações de
forma assíncrona e ativa um ou mais serviços de negócios para realizar o
processamento.
Um Service Activator é implementado normalmente como um Message-
driven Bean que consome via JMS as mensagens recebidas de um servi-
dor. Ao receber uma solicitação, o Service Activator analisa a requisição
e chama os componentes de negócios apropriados para processar a
solicitação. Após concluir o processamento, o Service Activator pode ou
não enviar uma resposta para o cliente, que pode informar se o processa-
mento foi bem-sucedido e fornecer os resultados.
Seguem algumas razões para um serviço ser implementado de forma
assíncrona:
-
gado é quando a chamada a um serviço é na verdade o aciona-
mento de um evento que não necessita que uma resposta seja
enviada ao cliente;
longos, é ruim deixar o cliente aguardando, pois faz parecer que o sistema está lento. Nesses casos, envia-se uma resposta imediata anunciando que a requisição foi recebida com sucesso e está sendo processada. Enquanto isso, envia-se uma mensagem JMS para uma fila para que o processamento seja feito de forma assíncrona;
-mente de aplicações que não se tem controle, o uso do processamen-to assíncrono evita que o desempenho seja degradado devido a uma demora no acesso a esse outro serviço;
grande de requisições de uma só vez e o processamento de todas em paralelo pode consumir os recursos da máquina de forma a com-prometer o desempenho como um todo. O uso de processamento assíncrono nesse caso coloca em uma fila essas requisições, que são processadas por um número fixo de threads. Isso limita a quantidade de recursos que aquele tipo de processamento pode consumir.
O padrão Service Activator também possui a vantagem de desacoplar
quem envia mensagem de quem a recebe. Isso possibilita que, por exem-
plo, a mensagem seja consumida independentemente por dois Message-
driven Beans diferentes, o que torna simples a inserção ou a exclusão de
cada um deles.
Este padrão (que também faz parte dos Core J2EE Patterns) propõe
centralizar a lógica de negócios através dos componentes e serviços
de várias camadas de negócios. É comum encontrar aplicações em-
presariais que possuem alguns componentes comuns, como business
objects e façades de serviço (tanto Session Facades quanto POJO
Facades) que são essencialmente uma camada fina sem lógica de ne-
gócios. Os facades expõem uma interface que controla o acesso dos
clientes aos objetos de negócio, e os business objects colaboram en-
tre si para executar a lógica de negócios que deve ser implementada
pela aplicação. Os business objects representam a implementação de
um modelo de domínio conceitual e concentram a lógica de negócios.
Em algumas situações, casos de uso podem requerer alguma lógica
extra para que executem corretamente suas tarefas. Preferencialmen-
te, os facades de serviço não devem conter lógica de negócios, e os
business objects, idealmente, não devem conter lógica de negócios
específica de um caso de uso, contendo somente a lógica específica
do modelo de domínio. Dessa forma, há mais reuso, e o acoplamento
entre os business objects diminui. Assim, pode-se introduzir uma camada que concentra lógica específica e coordena as ações dos business objects para que esta lógica específica seja executada corre-tamente. A estes serviços dá-se o nome de Application Services, que são serviços que contêm lógica de negócios específica e podem até utilizar DAOs para lidar com dados persistentes.
Uma das estratégias de implementação deste padrão chama-se Application Service Command. Nesta estratégia, um Application Con-troller utiliza um Command que delega o tratamento da requisição a um Application Service. Em uma variante desta estratégia, o próprio Application Service pode implementar um Command e tratar direta-mente uma requisição.
A outra estratégia de implementação deste padrão chama-se Appli-cation Service Layer, que propõe categorizar os Application Services com base em suas funções e capacidade de reutilização. Por exemplo, a primeira camada de serviços contém processamento de negócios específico de tipo de cliente (Channel Service). A próxima camada de
Padrão Service Activator
Padrão Application Service
35
Este é um padrão comportamental GoF, e descreve como construir
uma cadeia de objetos para o tratamento de solicitações, em que
esses objetos possuem lógica específica para o tratamento de cada
tipo de solicitação. Os objetos da cadeia durante o tratamento da
solicitação, podem retornar ou repassá-la para o próximo objeto na
cadeia, até que a mesma chegue ao final. O padrão possui meca-
nismos que permitem facilmente adicionar novos objetos na cadeia
para compor o processamento.
O objetivo deste padrão é evitar o acoplamento do remetente com
a lógica de tratamento e aumentar a flexibilidade permitindo que
um objeto possa ser facilmente inserido ou removido da cadeia. No
padrão original, a solicitação é tratada por somente um objeto, mas
existem documentados vários casos em que os objetos da cadeia
participam como um conjunto do processamento.
O uso desse padrão é indicado para casos em que o processamento
de uma requisição deve ser composto por diversos passos. A es-
trutura do Chain of Responsibility permite que os passos possam
ser utilizados de forma independente e flexível na composição de
diversas cadeias.
Os filtros da API Java Servlet são um exemplo deste padrão. O Mé-
todo doFilter() da classe javax.servlet.FilterChain, cuja instância é
recebida pelo filtro, é o método que liga os objetos na cadeia. Esse
método deve ser chamado para que a solicitação possa seguir para
o próximo elemento da cadeia. Cada filtro pode executar seu pro-
cessamento antes e/ou depois da chamada deste método.
Padrão Chain of Responsibility
Essencialmente, o objetivo deste padrão é definir a estrutura de um algoritmo em uma superclasse, delegando a definição de parte desta estrutura a suas subclasses. Este padrão se aplica quando dois ou mais componentes possuem alguma operação similar, mas não demonstram reuso de uma interface comum. Se uma mudança for necessária na parte comum do algoritmo, todos os componentes sofrerão alteração.
A parte do algoritmo que não varia é definida em um método concreto (o template method, ou método modelo) em uma classe abstrata. A parte que varia é definida em métodos abstratos que são chamados dentro do método concreto, e cada subclasse deve possuir implementações específicas dos métodos abstratos. Dessa forma, o método concreto da classe abstrata pode se comportar de diferentes formas, e o código comum dos diferentes componentes não precisa ser duplicado.
Este padrão é particularmente aplicado em grande escala em
frameworks. Na API da própria linguagem Java pode-se verificar vários exemplos da aplicação desse padrão. Um exemplo é a classe java.io.InputStream, que possui alguns métodos concretos, que invocam o método abstrato read(). Suas subclasses, como java.io.FileInputStream e java.io.ByteArrayInputStream, definem imple-mentações concretas deste método.
Deve-se tomar cuidado com o uso desse padrão, pois ele utiliza herança e uma classe só pode estender outra classe uma vez. Isso faz esse padrão adequado em casos em que pequenas variações do algoritmo principal irão acontecer de uma classe para outra. Em casos em que existem diversas possibilidades que podem ser combinadas nas implementações, é melhor recorrer a padrões que utilizam composição como State ou Strategy.
Para efeito de curiosidade, este padrão também é referenciado como princípio de Hollywood: “não nos telefone, nós telefonaremos para você”.
Padrão Template Method
serviços contém lógica de negócios específica de caso de uso (Use Case Services) e utiliza os Application Services genéricos (Generic Services), que constituem a próxima camada e contém lógica de ne-gócios que não é especifica de caso de uso ou cliente. A utilização de todas as camadas juntas nesta estratégia não é obrigatória.
A aplicação deste padrão traz as seguintes características a aplicação:
-
veis;
36 www.mundoj.com.br36
Co
nsi
der
açõ
es fi
nai
s
Ap
esar
de
hav
er v
ário
s fi
nai
s d
ifer
ente
s p
ara
a h
istó
ria,
acr
edit
o q
ue
nin
gu
ém p
erd
e (m
esm
o q
uem
foi
des
ped
ido)
, poi
s se
mp
re s
e g
anh
a em
con
hec
imen
to. D
epoi
s d
e le
r u
ma
vez,
rec
omen
da-
se q
ue
o le
itor
te
nte
exp
lora
r ou
tras
pos
sib
ilid
ades
e o
utr
os c
amin
hos
par
a ve
r qu
ais
seri
am a
s co
nse
qu
ênci
as p
ara
um
a se
qu
ênci
a d
e es
colh
as d
ifer
ente
s.
Este
tip
o d
e m
ater
ial s
obre
pad
rões
est
imu
la n
ão s
ó os
leit
ores
a c
o-
nh
ecer
em o
s p
adrõ
es, m
as a
exp
erim
enta
rem
su
as e
scol
has
em
um
am
bie
nte
sim
ula
do.
Iss
o ex
erci
ta n
ão s
ó h
abili
dad
es d
e m
odel
agem
d
e so
ftw
are,
mas
tam
bém
a a
nál
ise
crít
ica
dos
req
uis
itos
não
-fu
nci
o-
nai
s. O
s ex
emp
los
de
pad
rões
em
pre
gad
os e
m s
itu
açõe
s em
qu
e el
es
não
são
a s
olu
ção
mai
s ad
equ
ada
mos
tram
com
o as
con
seq
uên
cias
p
odem
ser
ru
ins
e p
reju
dic
ar o
an
dam
ento
de
um
pro
jeto
.
Na
vid
a re
al,
infe
lizm
ente
, m
uit
as v
ezes
não
dá
par
a vo
ltar
atr
ás e
to
mar
um
a d
ecis
ão d
ifer
ente
. D
essa
for
ma,
tod
os d
evem
est
ud
ar e
an
alis
ar b
asta
nte
an
tes
de
um
a es
colh
a. C
omo
na
his
tóri
a, o
fin
al ir
á d
epen
der
de
você
!
Referências
- - -
PLo
P 2
008
con
fere
nce
pro
ceed
ing
s.ce
pro
ceed
ing
s.p
roce
edin
gs.
Top Related