Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
Regras de Negócio
.
E
jCompany
Introdução
- Nota para ramo Facelets (jCompany 5.5) e versão 6.0 Preview (para Rally)
Este capítulo possui alguns conteúdos referindo-se às tecnologia JSP e Tiles, além do uso do framework
javascript DOJO, todas tecnologias utilizadas até a versao 5.2.
Mais recentemente, a partir da versão 5.5 e na versãso 6.0 Preview (utilizada apenas para o Rally Java
EE Open Source), estas foram aprimoradas em função do surgimento do Facelets como parte do padrão
Java EE 6 (JSF 2.0) e da popularização do mecanismo de "Java Não Obstrutivo" do jQuery.
Apesar disto, a grande maioria do conteúdo e mesmo as explicações e diagramas presentes no capítulo
se mantém úteis, até inclusive a versão 5.5. Há plano para revisão integral deste capítulo, por advento
do lançamento da versão oficial 6.0 do jCompany, prevista para 2011.
*
Figura E16.1. Categorias de classes/camadas participantes de uma seqüência de atendimento .
#1. Em uma aplicação Web, o processamento principal ocorre no servidor. Por este motivo, o
código Javascript que eventualmente roda em Navegadores, na máquina do usuário (cliente),
não é normalmente considerado, em uma análise de arquitetura MVC. Isto tem mudado, nos
últimos anos, com a popularização das tecnologias Ajax e da Web 2.0 em geral. Por este
motivo, iremos representá-la em nossos diagramas.
#2. A “camada Visão do Servidor” é representada por diversos frameworks de base integrados
pelo jCompany FS Framework, para “tirar o máximo” de uma Interface com o Usuário via
Web. Dentre eles, o JSF via componentes Apache Trinidad, o Tiles, o JSP e o JSTL, dentre
outros.
Não se pode destacar uma única classe ou artefato que represente um papel preponderante
nesta camada. Diversas especializações de componentes Apache Trinidad, JSPs de leiaute e
componentes de leiaute Tiles, Tag-Files e Tag-Libs (Struts e JSF), dentre outros, atuam com
interação, nesta camada.
*
As classes representadas no diagrama são implementações concretas ou interfaces que possuem algum papel de destaque em sua
camada. As implementações reais são bem mais sofisticadas, como veremos, muias vezes envolvendo interações entre dezenas classes
e tecnologias, em uma mesma camada.
#3. A “camada Controle” cuida da maior parte de processamento de internacionalização, cookies,
conversões e adaptações diversas, além de manter o “estado” (ou conversação) das transações
mais comuns. Trata-se de uma camada bastante complexa, especialmente quando se pretende
produzir GUIs sofisticadas via Web.
#5. A “subcamada de Serviço” contém a maior parte do código Java que realiza a orquestração
principal de retaguarda dos Casos de Uso Padrões do jCompany, para tratamento dos eventos
de manutenção do ciclo de vida para agregações complexas de objetos de domínio, como
vimos até aqui.
Mais especificamente, a classe “PlcBaseBO” traz 90% das implementações generalizadas para
este fim, nesta subcamada*. Uma descendente "AppBaseManager", gerada de forma
específica em cada aplicação, também atua via IoC, de forma similar a outras camadas, para
prover ponto de extensão e isolamento.
#6. A “camada de Domínio” foi muito utilizada ao longo dos tutoriais, trazendo programações de
decisão no escopo das agregações de classes que representam conceitos do negócio, sem
depender de outras camadas ou orquestrar transações.
*
Além desta categoria de classes que lidam com manutenção de ciclo de vida de documentos (também chamadas de Managers no
jCompany), encontraremos também, nesta subcamada, classes de categorias conhecidas como Application Servers (AS),
orquestradoras de lógicas de programação “batch” ou outros serviços de negócio, não necessariamente vinculados a “ciclos de vida” de
agregações. Neste caso, não há comportamento generalizado, por isto esta categoria não foi representada.
†
O sufixo “VO” é mantido para compatibilidade com versões anteriores, mas isso não implica que estas classes devam seguir o DP
Value Object (VO). Seu funcionamento deve seguir orientações de Domain-Driven Design (DDD), como vimos, e inclusive com sufixos
personalizados nos descendentes.
camadas. Deste modo, permite que sejam definidas variações de implementação de
persistência para, por exemplo, persistir para arquivos convencionais XML, LDAP ou até mesmo
Web-Services, sem afetar nenhuma das demais camadas, discutidas até aqui!
#8. A subcamada 8 é representada pelo framework que provê os serviços de persistência padrão
para SGBD-R relacionais. Ela é evidenciada no diagrama devido ao papel preponderante que
possui, eliminando códigos de INSERT, UPDATE e DELETE das classes de DAO, bem como
montando grafos de entidades de Domínio na memória a partir de “Result Sets JDBC” (deste
modo, viabilizando programações OO com produtividade).
Analisando as seqüências representadas pelos ítens 9 e 10, podemos nos surpreender com alguns
detalhes: a ordem de processamento pode não ser bem a inicialmente esperado, para quem está
iniciando na arquitetura MVC2.
Na prática, é bem mais fácil compreender uma arquitetura MVC1, na qual uma requisição é recebida por
um artefato específico de camada Visão (como uma “JSP”). Na variação MVC2, um único Servlet
“controlador” (DP Front Controller) é quem recepciona todas as requisições – a camada Controle é,
portanto, a responsável por iniciar o tratamento das requisições.
Vamos acompanhar, por exemplo, nesta numeração alternativa da Figura E16.1, a seqüência de uma
requisição GET, que é a que ocorre quando o usuário clica em algum item de menu, por exemplo. Neste
caso, vamos acompanhar uma Colaboração Padrão “plcTabular”, utilizada para realizar um Caso de Uso
“Manter Classe”.
2. S2. O comando HTTP GET com o endereço da transação digitado (URL) é recepcionado pelo Servlet
Controller “FacesServlet”, que por sua vez utiliza mecanismos declarativos padrões do JSF,
definidos nos arquivos “faces-config.xml”, para despachar a requisição para classes de controle mais
“especializadas”. Nos Casos de Uso padrões do jCompany, quando não há classe de controle
especificamente declarada no metadados da Colaboração, o tratamento é delegado para a classe
“PlcBaseJsfAction” ou “AppAction” (sua descendentes em escopo de aplicações). Isto é realizado
após um trâmite inicial que será detalhado mais a frente.
*
É importante notar que a classe "PlcBaseDAO", apesar de abstrata, traz códigos comuns entre Hibernate e JPA. Portanto, o DP
Abstract Factory, no jCompany, deve ser utilizado somente para customizações relativas ao uso destas tecnologias de mapeamento
objeto-relacional, tais como adaptações para variações de versão. Para variações mais radicais, para mecanismos de persistência não
relacionais, por exemplo, deve-se reimplementar o contrato “IPlcDao”.
para a camada Modelo, via contrato “IPlcFacade”, passando informações de contexto da transação
(metadados relevantes, usuário, etc.) e o tipo (Entidade Raiz) da classe principal envolvida.
Este algoritmo, somente na primeira requisição de cada “tipo”, tenta localizar o melhor “serviço”
(classe da hierarquia padrão) da camada Modelo para atender à requisição. Este algoritmo faz a
localização, por default, baseado em amarração automática a partir de padrão de nomenclatura
(Pattern Based Auto-Wiring IoC), que será explicado mais a frente. Se não encontrar nenhuma
classe específica para “atuar” na requisição em questão, utiliza a classe genérica “PlcBaseBO” (ou
alguma descendente, que pode ser declarada como padrão).
4. S4. A classe “PlcBaseBO”, neste caso, somente chama o serviço de recuperação simples de
objetos, chamado “recuperaTodos”, definido no contrato “IPlcDAO”.
Mas aqui também é utilizado IoC com amarração automática, de modo que um DAO específico e
com nomenclatura padrão, se existir, será ativado, facilmente expandindo ou alterando
implementações.
5. S5. Quaisquer das implementações de DAO irá iniciar seu trabalho recuperando uma conexão de um
pool de conexões JDBC via endereço “JNDI” declarado no arquivo “web.xml” ou outro mecanismo
disponibilizado pelo Application Server.
8. S8. Programações de pouca relevância serão feitas no caminho de volta da camada Modelo, mas a
finalização do serviço de fachada merece menção, pois é aqui que a transação com o SGBD-R é
encerrada. Na prática, uma rotina AOP do jCompany implementada na classe
“PlcAOPModeloCallbackService” intercepta chamadas às implementações de interfaces de
fachada, e ao final delas faz uma chamada adicional ao método “dao.rollback” ou “dao.commit”,
métodos disponíveis no contrato da classe “PlcBaseDAO”.
9. S9. A classe de controle costuma trabalhar um pouco mais que as demais no retorno, já que deve
montar uma estrutura de resposta para o usuário. Uma boa parte deste trabalho é feita nesta
camada, mas a maioria é delegada para a camada Visão.
Em linhas gerais: após decidir qual a “visão de retorno” será utilizada, analisando a declaração de
fluxo definida em arquivos como “faces-config.xml” (ou “struts-config.xml”, para tecnologia
Struts), a camada controle delega a responsabilidade de renderizar o documento de resposta para
classes da camada de Visão.
10. S10. As classes da camada Visão, formadas por uma combinado de frameworks e tecnologias
harmonizadas pelo jCompany, cumprem então o seu objetivo final de montar e renderizar um
documento HTML ou XHTML (se o cliente for um Navegador Web), acompanhado por rotinas
Javascript, CSS, mídia, etc., - e com dados relevantes devidamente “preenchidos”, incorporados em
textos ou em valores de campos de formulários HTML.
Perceba que, em uma seqüência típica MVC2, a camada Visão será a última a atuar. Em uma
atualização, componentes JSF de camada Visão entrariam com algum processamento na seqüência
de requisição (Empilhamento), mas 90% do processamento desta camada ainda se daria na
seqüência de resposta (Desempilhamento).
11. S11. Para um estudo convencional de MVC2-P, a seqüência de processamento estaria encerrada.
Porém, sabemos que ainda ocorrerão processamentos importantes relacionados à interface com o
usuário, através de rotinas Javascript que executam após a carga (onload) do HTML, por exemplo,
ajustando foco de campo, redefinindo Hot-Keys, alterando estilos dinamicamente, etc..etc.
Como dissemos, o processamento da camada Visão do Cliente não pode mais ser ignorado. Nos
últimos anos, tem crescido em popularidade e importância.
A Figura E16.2 mostra uma representação completa do Diagrama de Seqüência em discussão, com
alguns pontos adicionais em destaque.
*
Existem alguns “anti-patterns” de gerenciamento de sessões de persistência, que apregoam deixá-la aberta durante toda a requisição
HTTP (incluindo camada controle!), que chegam a dar arrepios de tantas possibilidades de problemas que podem causar. Infelizmente
mesmo Gavin King, o criado do Hibernate, chegou a aventar esta como uma possível “técnica”, em seu primeiro livro “Hibernate in
Action”. Mas hoje já se compreende os horrores que este alto acoplamento e falta de divisão de responsabilidade pelas camadas MVC
podem trazer, à exceção das mais simples aplicações, de um tipo que não é foco deste livro.
Figura E16.2. Diagrama de seqüência para “Manter Classe”. Eventos “F9-Pesquisar” e “F7-Novo”.
#1. Se o usuário clica em “F9-Pesquisar”, perceba que a seqüência disparada será a mesma da
chamada do menu. Isto é porque esta operação de pesquisa é default para este padrão
“plcTabular”. Mas no segundo caso, do clique no botão, o comando HTTP enviado será um
“POST” e será também enviado via “Ajax”*. Deste modo, evita-se das vezes subseqüentes a
renderização completa da página HTML†.
#2. Um novo fluxo exibido no diagrama de seqüência da Figura E16.2 é o do evento “F7-Novo”.
Perceba que ele também é enviado via Ajax. Uma outra vantagem desta arquitetura de
comunicação do jCompany é a de se não “sujar” a História do Usuário, mantida pelos
Navegadores. Como percebemos em tutoriais passados, nenhuma destas submissões de POST
subseqüentes, por serem utilizadas com Ajax, gera entradas nesta história, tornando-a útil‡.
#3. Tal como no caso da primeira chamada, a requisição é sempre atendida pela camada Controle.
Mais especificamente, como veremos em mais detalhes, por classes do tipo “Filter”
(“PlcMasterFilter”) e “Listener” (“PlcPhaseListener”), pelo Servlet de DP Front-Controller
“FacesServlet” e, para o processamento principal, para a classe central da camada Controle,
a “PlcBaseJsfAction”.
#4. Agora vemos uma segunda situação onde instâncias de objetos de Domínio são criadas. A
primeira ocorreu pelo Hibernate ou JPA, ao recuperar informações a partir a abertura ou
pesquisa explícita pelo usuário. Agora, o próprio jCompany produz instâncias da agregação
desejada, para “gerar entradas vazias correspondentes”, no formulário.
#5. Ao final, o HTML traz mais entradas (linhas ou itens) em branco para o usuário, que é o
propósito do evento “F7-Novo”, neste padrão.
Vejamos agora, complementando, como seria o fluxo de gravação (“F12-Gravar”), também para a
Colaboração “plcTabular”, na Figura E16.3.
*
O usuário pode desativar esta função, mas este é o esquema padrão proposto de interface.
†
O ganho se dará, principalmente, devido à não renderização do menu Pull-Down, que é normalmente o maior pesado.
‡
Se desligar o uso de Ajax, os POSTs também passam a ser armazenados na história do usuário. O jCompany evita problemas
funcionais como quando o usuário clica no botão “Volta” (Back) dos Navegadores. No entanto, itens inúteis passam a poluir a história
de uso, na prática, tornando-a pouco útil.
Figura E16.3. Diagrama de seqüência para “Manter Classe”. F12-Gravar.
#1. A implementação de fachada agora realiza uma pequena tarefa de “adaptação”, interpretando,
em uma coleção, quais são as operações que deverão ser realizadas em cada objeto. Para
tanto, analisa a propriedade padrão “indExcPlc”, que armazena marcações possíveis na caixa
de exclusão do objeto, a presença ou não de Object-Id gerado, dentre outras técnicas para
interpretação do “estado atual de cada objeto”.
Ao final, decide por incluir (se forem novos objetos), alterar (se forem alterações em objetos
pré-recuperados) ou excluir (se forem objetos marcados para exclusão), delegando
individualmente esta tarefa, para a subcamada de serviços.
#2. O serviço de manutenção do ciclo de vida então é reutilizado, em laço, para cada objeto. Deste
modo as mesmas funções “inclui”, “altera” e “exclui” que atuam para manutenções de
apenas uma agregação de objetos são reutilizadas (Ex.: Manter Agregação Simples ou
Mestre/Detalhe)
#3. Serviços de persistência de nome correlato são também acionados e cada um utiliza o
framework base de persistência para gerar os comandos SQL necessários*.
#4. Neste evento, ao final da fachada, o encerramento de transação ocorrerá via chamada
“dao.commit()”, para confirmar as gravações, a menos que uma exceção seja disparada, o
que produziria um “dao.rollback()”†.
#5. O HTML final traz o conteúdo da lista atualizado (linhas excluídas removidas, alterações,
Object-id preenchido, etc.) e também a mensagem “Registro gravado com sucesso”.
*
o jCompany não produz SQLs, delegando esta atividade inteiramente para frameworks de implementações do padrão JPA tais como
o “Hibernate”.
†
Quando utilizando EJB3 e gerenciamento de transação pelo conteiner, ela também se encerrará neste mesmo ponto, conforme a
opção declarada de transação.
caso das Colaborações de Seleção e Consulta, o serviço usado será o “recuperaLista”, já que este
último permite a passagem de argumentos.
O trecho de Código E16.1 mostra um exemplo de como esta chamada é efetivamente implementada.
Código E16.1. Código de Controle, decidindo sobre qual serviço utilizar do contrato de fachada.
Note que, além de testar a delegação conforme o padrão corrente seja “plcTabular” (Logica.TABULAR)
ou não, este método de controle também verifica se uma seleção “paginada” está sendo utilizada,
situação em que uma terceira alternativa de serviço de fachada será utilizada. O Diagrama de Seqüência
correspondente para o evento “F9-Pesquisar” nas Colaborações “plcSelecao” ou “plcPesquisa” é
exibido na Figura E16.4.
#2. Por sua vez, a implementação da fachada para “recuperaLista” chave “recuperaListaQBE”, no
serviço de negócio.
#5. Na camada visão, o HTML gerado que será exibido para o usuário irá variar, naturalmente, em
comparação ao que geramos na pesquisa de “Manter Classe” - até porque, neste último caso, a
lista será para manutenção, e a nossa atual será utilizada para consulta apenas. Porém, em
ambos os casos os dados retornaram na forma homogênea de uma coleção de Entidades
“List<PlcBaseVO>”.
#1. A criação de novos objetos ocorre como no caso anterior, sem transações, mas agora não há
“laço” de criação para o objeto raiz, pois trabalhamos com apenas uma agregação por vez.
#2. A gravação é agora delegada para “gravaObjeto” (em vez de “gravaTabular”), e o serviço
de fachada utilizado é o “gravaObjeto” (em lugar de “gravaTabular”).
#3. Na fachada, há uma interpretação, baseada na comparação com a imagem anterior do objeto,
para decidir se ele será alterado ou criado (incluído). Em função disso, métodos distintos do
serviço de negócio serão acionados. Repare que, neste caso, a exclusão não é realizada
simultaneamente com a inclusão e alteração, mas por um evento próprio disparado pelo botão
“Excluir”.
Para o fluxo de exclusão, que neste caso é disparado por evento próprio, há uma seqüência inteiramente
nova.
#2. O “PlcFacadeImpl” verifica se há uma declaração para uso de “exclusão lógica” nos
metadados. Se existir, muda o rumo da transação para uma “alteração” (sitHistoricoPlc=”I”),
em lugar da exclusão física em si.
#3. Os comandos utilizados para comunicação com a camada de persistência são “delete” para
exclusão física e o “save”, para atualização.
#4. A transação é também encerrada no final do método de fachada, via AOP, com comando
“dao.commit()”.
#5. Se tudo transcorre corretamente, uma mensagem “Registro Excluído com Sucesso” é montada
em escopo de requisição (usando a interface “HttpServletRequest”) e a seqüência do evento
“novo” é chamada. Deste modo, o usuário recebe um formulário em branco com a mensagem
de confirmação da exclusão.
Como deixamos claro na introdução deste tópico, estas são visões gerais das seqüências de transação
para alguns eventos, simplificadas para compreensão dos mecanismos primários da arquitetura MVC2-P
generalizada no jCompany FS Framework. Iremos, ainda neste capítulo, colocar uma “lupa” em cada
uma destas camadas, para enxergá-la com um nível adicional de detalhamento.
As vantagens da arquitetura MVC2-P são reais, mas muitas vezes visíveis somente no médio prazo.
É uma arquitetura que nos permite modificar alguma de suas camadas radicalmente, com um
mínimo impacto nas demais – ao que chamamos de arquitetura “feita para durar” ou “à prova de
futuro” (future proof).
Vejamos a alguns exemplos reais de benefícios, postos à prova no próprio projeto do jCompany
Developer Suite, concebido somente com um framework, em 2002/2003.
Flexibilidade da camada Controle.
Graças à sua arquitetura MVC, a tecnologia JSF pôde ser incorporada para suportar o padrão Java EE
5 e novas tendências do mercado, apenas com a refatoração de uma parcela dos pacotes das
camadas Controle e Visão (“parcelas” porque, dentro das camadas Controle e Visão, já existiam
pacotes isolados com baixo ou nenhum acoplamento com a tecnologia Struts).
Flexibilidade da camada de Persistência.
*
Devido à ausência de classes para lidarem com tecnologias de “remoting” como Business Delegate e Service Locators nas fachadas,
Data Transfer Objects, etc..
†
Lembrando que o próprio Hibernate também pode ser usado como uma implementação JPA, indiretamente.
Como sempre houve arquitetura de delegação das tarefas de “renderização” do HTML para um
segmento específico de classes, específicas da camada Visão (mesmo em Struts, via Tag-Files),
uma empresa produziu uma saída alternativa em WML em pouco tempo, para atendimento a
dispositivos móveis, sem implicar em revisões ou impacto em outras camadas.
A Powerlogic produziu, na versão 2.7 do jCompany, uma implementação de referência para
integração com Lazslo, gerando XML em lugar de HTML*.
Neste instante, a Powerlogic está em vias de liberar uma opção de “sindicalização universal” de
documentos, que permitirá que quaisquer formulários, consultas ou seleções, desenvolvidos por
clientes em jCompany, possam ser sindicalizados em agregadores de conteúdo diversos, em
tecnologia RSS/RDF/Atom. E isso vale para aplicações já existentes, em versões anteriores.
Tudo isso foi possível, com baixo ou nenhum impacto em programações de camada Controle, e
nenhum impacto “da fachada para trás”!
*
O uso deste recurso foi depois desestimulado, com a popularização da tecnologia Ajax, pouco tempo depois. E novidades nesta área
estão previstas, para versões superiores à utilizada neste livro.
†
Méritos sejam dados a Rod Johnson, pioneiro nesta área com seu framework Spring e livros com dicas para substituição apropriada
dos excessos da tecnologias EJB 2.x.
‡
Note que a camada ortogonal de Domínio está presente em ambos os empacotamentos, no lugar dos DTOs dos EJBs 2.x.
§
Devido ao alto nível de automação em todas as camadas, com qualidade final de produção.
tecnologias de camada Visão (JSF, Ajax), camada de Controle (JBoss Seam), serviços de negócio e
camada Persistência (JPA, Hibernate).
Escala em complexidade sem perda de controle, ou “produtividade sustentada”.
Mas o benefício mais fundamental (e muitas vezes “subvalorizado”), do uso da arquitetura MVC é a
sua capacidade de encapsular especialidades de forma refinada, preservando um bom
entendimento e controle de aplicações corporativas, mesmo quando estas evoluem em
tamanho e complexidade.
Infelizmente, para os benefícios de uma arquitetura MVC aparecerem, será necessário o bom
uso da Orientação a Objetos* e também o passar do tempo. Em um projeto que irá produzir
a primeira versão de uma aplicação, por exemplo, pode-se não perceber nenhum ganho nítido,
nesta área† - por este motivo, vemos muitas vezes fornecedores contratados “por projeto”
produzindo código “espaguete”, altamente acoplado, pressionados para entregarem a “bomba
relógio” no prazo.
*
A arquitetura MVC não é a prova de programação “COBOL” embutida dentro das camadas!
†
E, infelizmente, muitas empresas buscam ansiosamente diminuir seu déficit de projetos, muitas vezes sacrificando arquitetura em
uma “primeira versão” rápida, que nas fases de manutenção cobram seu preço, com juros e correção monetária.
‡
Rapid Application Developement. Sigla utilizada para representar desenvolvimentos rápidos focados na produção de Interfaces com o
Usuário via “arrasta-e-solta” e “WYSIWYG”, onde componentes visuais são normalmente vinculados (binding) à camadas de
persistência.
§
Como sabemos, visando simplificação extrema para concorrer com a Microsoft no mercado “varejo” do “.NET”.
**
Apesar de visivelmente perigosa, alguns argumentam que esta arquitetura ainda respeita o MVC original, conforme definido na
época do Smalltalk. O problema é que uma implementação MVC rasa como esta não reforça a separação de aspectos e confia
demasiadamente na disciplina do Desenvolvedor – no que chamamos de recaída para o desenvolvimento “artesanal”. Esta variação
pode ser uma simplificação interessante para um segmento do mercado, mas não a recomendamos para o corporativo.
Programações da Camada Visão - Cliente
- Visão geral
Há inúmeras possibilidades de se implementar interações sofisticadas com o usuário hoje, através dos
Navegadores Web, com base nas tecnologias DHTML/Javascript e Ajax, para citar algumas. O jCompany
homologa frameworks como o DOJO, especializadas nesta área, bem como recursos do Apache Trinidad,
embutidos em seus componentes JSF, prontamente disponíveis para uso. Além disso, traz bibliotecas
Javascript/Ajax próprias, que realizam tarefas como:
Tab-Folder DHTML (comuta abas sem transmissão do Navegador);
Hot-Key dos botões;
Foco inteligente (posiciona no primeiro campo vazio do formulário, após a carga);
Envio de comandos POST via AJAX (F12-Gravar, F7-Novo, F9-Pesquisar);
Mensagens de advertências (“Tem certeza que deseja excluir...”);
Etc.
Para se realizar variações em Javascript, como vimos para o lançamento de “ProventoDesconto”, o
desafio passará não somente pelo conhecimento desta tecnologia mas, principalmente, por conhecimento
sobre como utilizá-la de uma maneira “cross-Browser”, que se comporte bem com a larga variedade de
modelos e versões destes Navegadores.
Vejamos a arquitetura básica sugerida para a confecção de rotinas Javascript específicas, sejam “puras”
ou através do framework DOJO.
...
@PlcConfigOtimizacao(javascriptEspecificoUsa=true)
...
Código E16.2. Metadados para declaração de uso de biblioteca Javascript “AppGeral.js”.
2. Edite agora o arquivo “AppGeral.js” (Control+Shift+R) e inclua nossa primeira função Javascript em
nível de biblioteca*, conforme o trecho de Código E16.3.
...
/**
* Javascript também merece documentação!
*/
function minhaPrimeiraFuncao() {
alert('Alerta de Teste');
}
...
Código E16.3. Código de Controle, Primeira função Javascript no AppGeral.js.
3. Para chamar esta função, após a carga da página principal da aplicação, por exemplo, edite a página
“/WEB-INF/jsps/principalIntroducao.jsp” e inclua o código em Código E16.4.
...
<%@ taglib uri="/WEB-INF/fmt.tld" prefix="fmt"%>
<fmt:setBundle basename="ApplicationResources"/>
<script>
// Registra a função Javascript para executar
setFuncaoOnLoad('minhaPrimeiraFuncao()');
</script>
<table width="500" border="0" cellspacing="0" cellpadding="0">
...
Código E16.4. Registro de função Javascript, para execução após a carga (renderização) da página.
Este código Javascript “embutido” chama uma função do jCompany “setFuncaoOnLoad”, que
por sua vez registra a nossa função Javascript para execução ao final da carga da página†.
*
Já fizemos uma função embutida em uma página, para o Caso de Uso “Manter Coleção”, em tutoriais passados. Aquela função, agora
que sabemos, deveria ser externada para o arquivo AppGeral.js, para dar ênfase a otimização.
†
A função “setFuncaoOnLoad” provê um tipo de registro de IoC em Javascript.
Figura E16.7. Alerta Javascript após a carga da página inicial.
...
<img id="bannerACME" style="-moz-opacity:0;filter:alpha(opacity=0)"
alt="ACME - RH Tutorial"
src="${pageContext.request.contextPath}/plc/midia/acme/acme_banner.gif" />
...
Código E16.6. Imagem “identificável” via Javascript e com estilos de opacidade.
Perceba que há dois códigos CSS diferentes, separados por ponto e vírgula, para fazer a mesma
coisa: um no padrão Mozilla e outro no padrão do Internet Explorer. Neste caso inicial, ambos
podem ser misturados, sem seqüelas.
2. Altere o conteúdo do arquivo “AppGeral.js”, no projeto “rhtutorial”, incluindo a nossa função
“minhaPrimeiraFuncao”, conforme o trecho de Código E16.7.
function minhaPrimeiraFuncao() {
if (NavYes) {
alert('Sou navegador!'); // 1
setInterval("MOZ()",50); // 2
}
if (ExpYes) {
alert('Sou explorador!');
setInterval("IE()",50);
}
if (Opr) {
alert('Não sou suportado - mas me comporto bem!'); // 3
}
}
valor=0; // 4
function IE(){
if (valor<=95) valor+=5; // 4
document.getElementById("bannerACME").filters[0].opacity=valor; // 5
}
function MOZ() {
if (valor<=95) valor+=5;
document.getElementById("bannerACME").style.MozOpacity=valor/100; // 6
}
...
Código E16.7. Código de Controle, de Nova função Javascript acionada no “onload” da página principal.
#1. As variáveis globais “NavYes” (Navigator / Mozilla), “ExpYes” (Explorer – IE) e “Opr" (Opera)
nos permitem identificar a família do Navegador corrente. Elas foram disponibilizadas pela
biblioteca Javascript jCompany. Um alerta foi incluído somente para depuração inicial*.
#2. A função Javascript “setInterval” irá chamar uma função com código específico de um dos
navegadores suportados, várias vezes, de acordo com o intervalo em milisegundos informado.
#3. O Navegador “Opera” não está exemplificado, pois não foi priorizado por nossa empresa
hipotética. Mas é importante notar que esta facilidade irá “degradar” bem para este caso - ou
seja, não haverá perda funcional, mas apenas estética, caso o usuário esteja utilizando este
Navegador.
#4. Uma variável global deve ser criada, para conter o nível da opacidade que será incrementado a
cada chamada. Note que basta declararmos a variável fora de qualquer função, para que seja
definida.
#5. A cada chamada de alguma das funções, ela incrementa 5% de aumento da opacidade
(diminuição da transparência), até que atinja 100, quando estará totalmente opaca (visível) e a
rotina não mais terá efeito.
#6. No caso do Mozilla, a mudança é feita através de estilos, e a escala é de 0 a 1, e não 0 a 100.
Por este motivo, uma divisão é realizada.
...
<plcf:botaoAcao id="botaoAcaoGravar" acao="grava" partialSubmit="#{requestScope.ajaxUsa}"
label="jcompany.evt.gravar" botaoArrayID="GRAVAR" rendered="#{requestScope.exibeGravarPlc=='S' and empty
requestScope.estiloApresentacaoPlc and requestScope.exibe_jcompany_evt_gravar!='N'}"
hotKey="#{requestScope.gravarHotKey}" alertaExcluirDetalhe="jcompany.alerta.excluir.detalhe.tabular" />
...
Código E16.8. Declaração do componente de botão para “F12-Gravar”, com submissão parcial via Ajax.
Perceba, pelo trecho de código em destaque, que uma “submissão parcial” Ajax é ativada, se e somente
se o usuário está com a opção “ajaxUsa” ativa. Esta opção pode ser ativada pelo próprio usuário,
dinamicamente, em “Personalização de Formulário”, e é default nos templates INI†. O jCompany
*
É sempre bom certificar-se de que sua versão mais nova de Navegador está sendo capturada corretamente, pois esta é uma
categoria de software em franca evolução.
†
Também é possível ao Desenvolvedor desativá-la globalmente ou localmente em uma Colaboração, via metadados.
realiza esta e outras configurações de modo a tornar o uso de Ajax imediatamente funcional para os
casos mais comuns.
Para mais informações sobre todas as possibilidades de uso de Javascript via Apache Trinidad,
recomendamos uma visita ao Web-Site deste projeto, disponível nas referências bibliográficas deste livro.
<td>
<script type="text/javascript">
// 1
if (typeof dojo=='undefined') {
document.write(
"<script type=\"text/javascript\" src=\"${pageContext.request.contextPath}/plc/javascript/dojo/dojo.js\"><\/script>");
}
</script>
// 2
<script type="text/javascript">
dojo.require("dojo.widget.Clock");
</script>
// 3
<div dojoType="Clock" label="Rh Tutorial" />
</td>
</tr>
...
Código E16.9. Código Javascript que introduz uma primeira declaração de importação do DOJO.
#1. No primeiro bloco de declaração Javascript, realizamos o teste padrão para importação
dinâmica do Javascript DOJO, que evita carregá-lo duas vezes (no caso de ele ter sido utilizado
em outros componentes de leiaute).
#2. Em outro bloco de Javascript (é importante que seja outro), indicamos as dependências
que iremos utilizar somente para o nosso caso, equivalentes ao “import” do Java.
#3. Finalmente, neste ponto, definimos uma nova divisão HTML “<div/>”, com o atributo
“dojoType” indicando o tipo “Clock”, um relógio disponível como Widget no DOJO. Um opção
de rótulo também foi definida em “label”.
*
Lembre-se que nós o retiramos de nosso exemplo “rhtutorial”, no momento em que aplicamos o Web-Design de rodapé “simplificado”
que fizemos, baseado em tabelas “<table>”. O FishEye naquele ponto iria requerer uso de divisões “<div>”.
3. Como estamos no mundo do Cliente, não há necessidade de liberação Maven quando editamos
somente JSPs e Javascript. Teste imediatamente o resultado de nosso código no Tomcat, que deve
aparecer agora como a Figura E16.8.
Este é um exemplo simplório, mas dá as dicas fundamentais do potencial deste novo mundo. Veja na
Figura E16.9, a relação de pacotes e Widgets do DOJO, indo desde relógios, calendários e utilitários
de animação, até rotinas de gráficos e editores multimídia.
Figura E16.9. Visão geral dos pacotes de bibliotecas Javascript do DOJO, homologados no jCompany.
Obs.: Os Widgets do DOJO são baseados na renderização de divisões “<div/>” com atributos
proprietários dinamicamente interpretados - inexistentes nos esquemas estáticos esperados para um
documento XHTML, conforme definido pelo W3C (tais como as propriedades “dojoType” e “label”, em
nosso exemplo). Este não costuma ser um problema para a grande maioria dos casos, pois estes
atributos são desprezados pelos Navegadores. Mas para empresas que exigem documentos HTML com o
formato “Strict”, esta será uma área de conflito, pois verificadores de formato rejeitarão documentos
que usam este padrão do DOJO como Strict válidos*.
*
Note que, mesmo assim, os Navegadores interpretarão os Widgets corretamente, mesmo utilizando formato Strict! A ressalva diz
respeito somente a validadores de formatação que visam garantir “boas práticas” no formato Strict.
Programações da Camada Visão - Servidor
- Visão geral
Vamos agora nos mudar para o mundo do servidor. Como vimos, é aqui onde os objetos da “camada
Visão do Servidor” renderizam documentos HTML ou XHTML para navegadores Web e, possivelmente,
outros formatos tais como WML para dispositivos móveis ou mesmo XML para integrações diversas.
De um modo geral, um grande objetivo da camada Visão é conter o mínimo possível de lógicas
procedimentais. Para tanto, usamos a camada de Controle para absorver tanto processamento quanto
possível. Porém, algum tipo básico de condicionais e laços serão inevitáveis. Eles poderão aparecer em
componentes JSF do Apache Trinidad ou mesmo em JSTL, ambos sobre a tecnologia JSP*.
Do ponto de vista tecnológico, já vimos que o jCompany usa JSP 2.0, JSF 1.2 e JSTL 1.1, em sua
versão atual (5.1, no momento da escrita deste livro) de forma integrada. Todas estas tecnologias
possuem mecanismos de “programação” possíveis, para a camada Visão†.
Vejamos, nos tópicos seguintes, exemplos de programações típicas de camada Visão do Servidor, na
arquitetura do jCompany FS Framework.
#2. Atributo “rendered”, implementando a regra: “CPF para funcionários que não são solteiros é
confidencial”*.
*
Deve-se evitar a linguagem Java nesta camada. Experimentalmente, esta prática já se comprovou como inadequada.
Desenvolvedores que a utilizam tendem a não respeitar claramente a separação das camadas Controle e Visão, com algoritmos
excessivamente complexos, miscigenados às lógicas de renderização de páginas.
†
A opção pelo uso do JSF integrado às JSPs preserva um grande leque cultural e ainda possibilita a gestão de leiautes OO com
IoC, via Tiles. O uso de alternativas de leiaute mais recentes como “facelets”, por exemplo, não está descartado pela Powerlogic
(podendo inclusive ser realizado imediatamente, através de customizações), mas estava com prioridade baixa no momento desta
escrita, uma vez que as JSPs no jCompany se encontram em estágio bastante avançado em termos de simplicidade.
#3. Como estamos eliminando condicionalmente uma coluna, precisamos de uma alternativa para
que nossa lista não apareça com deslocamentos indevidos. Assim, na condição inversa,
estamos exibindo condicionalmente e em vermelho, o termo “[Confidencial]”.
Obs.: As Tag-files do jCompany, utilizadas nas JSPs para a tecnologia Struts, implementam um
mecanismo similar ao “rendered” do Trinidad, através da propriedade “exibeSe”.
...
<c:if test="${empty requestScope.passoAssistentePlc or requestScope.passoAssistentePlc==3}">
<plcf:linha>
<plcf:celula>
<plcf:titulo tituloChave="label.temCursoSuperior"/>
<plcf:caixaMarcacao id="temCursoSuperior"
value="#{plcEntidade.temCursoSuperior}" />
</plcf:celula>
</plcf:linha>
</c:if>
...
Código E16.10. Programação de camada Visão com JSTL Core “if”
Não iremos descrever todas as possibilidades de “controle de fluxo” das Tag-Libs Core JSTL e nem dos
componentes JSF. De qualquer modo, é uma boa prática manter o uso de tais recursos em seus
níveis básicos. Suspeite da necessidade de programações mais intrincadas neste ponto: elas
não podem ser “movidas” para a camada Controle?
- Visão geral
De uma maneira simplificada, a camada Controle deve conter programações relacionadas à interface com
o usuário, segurança, navegação e validações de entrada, não incluindo regras do negócio e nem de
acesso a bases de dados, por exemplo.
Muito embora não tenham acesso direto a sessões de Persistência, as classes de Controle devem ser as
únicas com acesso aos objetos Web, tais como HttpSession, HttpServletRequest, etc.) e a certos
frameworks especialistas nesta camada, tais como JSF/Apache Trinidad (ou Struts), JBoss Seam ou Tiles
(parte de Controller).
*
Não me perguntem por que, raios, existiria uma regra como essa!
Para facilitar o estudo das seqüências de atendimento, podemos dividir o trabalho da camada de Controle
em duas metades distintas:
Fase de Empilhamento (equivale às fases JSF de Requisição e de início da Resposta): Nesta
fase “de ida”, as classes de controle recepcionam requisições advindas dos Navegadores, validam os
dados recebidos e os transformam em estruturas de objetos, com semântica mais rica e apropriada
para o prosseguimento da requisição. Iniciando o processamento da resposta, os métodos de
controle chamam serviços de fachada, que encapsulam regras de negócio e acesso à persistência.
Fase de Desempilhamento (equivale à fase JSF de finalização da Resposta): Nesta fase “de
volta”, informações devolvidas da camada Modelo são recebidas e recebem manipulações eventuais,
incluindo tratamento de cenários de exceção e análise de fluxos de desvio declarados. Regras de
programação associadas ao mundo da Interface com o Usuário podem ser realizadas, quando estão
acima do escopo de componentes JSF individuais. Deste modo, ao delegar o processamento de
renderização dos documentos de resposta para a camada Visão, resta a esta última camada
somente um trabalho de programação essencial, realizável através de primitivas simples como
assertivas e laços.
Figura E16.12. Seqüência mais detalhada de requisição GET para colaboração “plcTabular”.
#1. Chamada via menu: Como vimos anteriormente, este tipo de evento envia um comando
HTTP GET para a camada Controle tratar. Nestes diagramas mais detalhado, abstrairemos da
representação Javascript utilizada na introdução.
#2. Os filtros que atuam são agora explicitados. Classes do tipo “Filter” foram introduzidas na
especificação “Servlet 2.3”, para atuarem “em torno de um Servlet”; por isso, são as que
primeiro executam processamento, após a chegada de uma requisição*.
Muito embora estas classes, na maioria dos casos, não requeiram atenção e especialização,
haverá situações onde compreender a sua atuação será importante. Os seguintes filtros são
registrados e atuam nesta fase:
“PlcMasterFilter”:
*
No jCompany, classes de filtro são nomeadas com a convenção “Plc[Nome]Filter”.
“utf-8”, em cada requisição HTTP. Deste modo fica-se independente de configurações de cada
Application Server, que podem utilizar “latin-1” ou outro padrão que não corresponderá ao
utilizado pelo jCompany.
#4. Perceba que, a partir deste ponto, classes descendentes e especificas de cada projeto, em
esquema de Inversão de Controle, passam a atuar, tais como "AppPhaseListener" e
"AppAction". A classe “AppPhaseListener”, descendente de “PlcPhaseListener”, é incluída
mesmo que sem implementações específicas, pelos templates INI, para facilitar e promover
especializações neste ponto, de forma padronizada.
O métodos “beforePhase” e “afterPhase” são disparados pelo Apache Trinidad, que realiza
os trabalhos de recepção e reconstrução do “estado” dos componentes JSF (basicamente, a
reconstrução da árvore de componentes no servidor, conforme estabelece o padrão JSF). O
jCompany atuará, através da classe “PlcPhaseListener”, com implementações genéricas nos
métodos “beforePhase” e “afterPhase” para o evento “RESTORE_VIEW 1”*.
Na prática, como nossa requisição específica é um mero “GET” e não envia mudanças de
estado significativas em componentes JSF, esta fase de “RESTORE VIEW” é bastante trivial.
Para os interessados em maior aprofundamento nesta área, sugerimos o estudo deste ciclo de
vida em literatura específica sobre JSF.
#5. É neste ponto, após a recriação no servidor do estado de componentes JSF, onde se inicia a
fase de renderização da resposta para o usuário. Ou seja, a partir deste ponto já sabemos o
que o usuário quer e validamos sua solicitação – vamos então prosseguir para conseguir
montar um documento com a resposta apropriada. Mas note que, de um ponto de vista de
mais baixo nível, ainda estamos “empilhando” chamadas de métodos de objetos, na
pilha da memória (Heap).
*
Segundo o contrato de interceptação padrão do JSF, os diversos eventos de ciclo de vida de componentes são passados como
argumento, para os mesmos métodos “beforePhase” e “afterPhase”.
#6. De forma simplificada*, a classe “PlcPhaseListener” termina por delegar para o método
“inicializaManagedBeans” a tarefa de obter instâncias do objeto que encapsula metadados
do jCompany, do objeto que mantém em memória os dados transmitidos (em nosso caso, não
transmitimos nenhum) e também do objeto que irá realizar o processamento de Controle,
específico para a requisição (nossa classe no padrão Action).
#7. Neste método, no momento em que o processamento tenta interpretar uma expressão para
obter referência ao objeto que encapsula os metadados do jCompany, o JBoss Seam entra
em ação. A partir deste ponto, precisamos de conhecimentos básicos sobre este framework†.
Tudo começa porque o objeto de metadados em questão, cuja referência tentamos obter
dinamicamente via o interpretador de expressão EL
“PlcElHelper.getInstance().evaluateExpressionGet” é “gerenciado pelo jBoss Seam”.
Este objeto é declarado como variável de instância da classe "PlcBaseJsfAction", e as
anotações reproduzidas no Código E16.11 declaram este “gerenciamento”.
...
@In(value = PlcJsfConstantes.PLC_CONFIG_URL_COLABORACAO, required = false, scope = ScopeType.EVENT)
@Out(value = PlcJsfConstantes.PLC_CONFIG_URL_COLABORACAO, required = false, scope = ScopeType.EVENT)
protected PlcConfigUrlColaboracao configUrlColaboracaoPlc;
...
Código E16.11. Declaração de objeto de metadados “gerenciado” pelo JBoss Seam, no “PlcBaseJsfAction”.
...
@Factory(PlcJsfConstantes.PLC_CONFIG_URL_COLABORACAO)
public void criaConfigUrlColaboracao() throws PlcException {
...
Código E16.12. Declaração de método “factory” para objeto de metadados no “PlcBaseJsfAction”, para o
jBoss Seam.
#9. Assim, da primeira vez que a aplicação tenta acessar uma Colaboração padrão em uma
conversação‡ e seus “metadados” ainda não existem disponíveis para acesso, o JBoss Seam
aciona este método de factory. Este método, somente uma primeira vez na vida de uma
aplicação§, obtém as anotações de metadados tanto da camada Controle quanto Comuns
(Domínio) para esta Colaboração e as mantém encapsuladas, para facilitar o acesso do
framework.
*
Já que existem várias chamadas do “beforePhase”, até este ponto que nos interessa.
†
A representação da chamada de “AppPhaseListener” para o JBoss Seam no Diagrama de Seqüência está em alto nível de abstração,
apenas para fins didáticos. O mecanismo real é mais intrincado e também poderoso.
‡
Uma escopo de “conversação” do JBoss Seam é formado por um conjunto de requisições HTTP que compartilham de um mesmo
“estado”. Este estado é gerenciado pelo jBoss Seam, de forma equivalente a uma sessão HTTP, porém normalmente de menor duração.
§
Isto somente é realizado uma única vez, já que as informações de metadados não variam a cada requisição e nem mesmo durante a
vida da aplicação. Portanto, elas ficam armazenadas em caching na primeira requisição do usuário e são reutilizadas da memória, daí
por diante.
- O objeto “plcLogicaItens”, de classe “List<PlcBaseVO>” será utilizado para Colaborações
no padrão “plcTabular”, “plcCrudTabular”, “plcSelecao” e “plcConsulta”, ou seja, para aquelas
que manipulam coleções de entidades,
...
@In(value = PlcJsfConstantes.PLC_LOGICA_ITENS, required = false, scope = ScopeType.CONVERSATION)
@Out(value = PlcJsfConstantes.PLC_LOGICA_ITENS, required = false, scope = ScopeType.CONVERSATION)
protected PlcBaseLogicaItens logicaItensPlc;
...
Código E16.13. Declaração de objeto que encapsula listas para as Colaborações “plcTabular”,
“plcCrudTabular”, “plcSelecao”, “plcConsulta”, gerenciado pelo jBoss Seam, em “PlcBaseJsfAction”.
#11. Como a colaboração de nosso exemplo é “plcTabular”, o jCompany tenta capturar dados no
objeto “plcLogicaItens”. Se eles não existirem (como no caso da primeira requisição), o
método “criaLogicaItens”, definido como “factory JBoss Seam” para este objeto, é
acionado, iniciando uma conversação.
...
@Factory(PlcJsfConstantes.PLC_LOGICA_ITENS)
@Begin(join = true)
public void criaLogicaItens() throws PlcException {
...
Código E16.14. Método de factory JBoss Seam para “plcLogicaItens”, em “PlcBaseJsfAction”.
#12. A implementação deste método irá variar, dependendo do padrão da Colaboração corrente.
Para o caso do “plcTabular”, como a operação default é a recuperação de todos os objetos, o
processamento de criação será delegado para o método “pesquisa” (o mesmo acionado
quando o usuários clica em “F9-Pesquisar”).
O método “pesquisa” obtém, por fim, uma referência do contrato de fachada padrão
(“IAppFacade” – “AppFacadeImpl”) e chama um serviço da Camada Modelo/Persistência, para
recuperação da lista de Entidades.
#1. Após o recebimento da coleção recuperada via serviços da camada Modelo, o método
“pesquisa” simplesmente disponibiliza a lista de objetos retornada no objeto “plcLogicaItens”*,
disponível como variável de instância da própria classe†.
#4. Uma nova fase JSF é iniciada e o método “afterPhase” é executado para o evento
“RENDER_RESPONSE”, ainda na classe “PlcPhaseListener”.
#5. A partir deste ponto o processamento é delegado para o Tiles, que através de uma JSP de
Front-Controller “tilesDispatchPlc.jsp”§ começa a etapa de “despacho” de conteúdo HTML. O
uso da JSP é necessário como técnica de integração do Tiles com o JSF e seu código é de
extrema simplicidade, realizando uma de duas coisas:
- Se a Colaboração corrente estiver utilizando o leiaute Universal do jCompany, esta página irá
inserir uma definição Tiles “def.universal.automatico” (utilizando a Tag “tiles:insert”).
- Se a Colaboração corrente estiver utilizando um leiaute específico, ela irá tentar inserir uma
definição Tiles com mesmo nome que a URL (Ex.: “uf”).
#6. Definições de leiaute Tiles são recursivas, como já vimos. A definição principal
“def.universal.automatico”, por sua vez, inclui referências a outras subdefinições de
forma dinâmica, com base em convenções de nomenclatura.
*
Dependendo do padrão, poderia ser uma Agregação de Objetos, disponibilizada no objeto “plcEntidade”.
†
Note que, em JSF, as classes de Action do jCompany são statefull em escopo de “conversação JBoss Seam”, aceitando objetos que
contém dados como variáveis de instância - ao contrário do Struts, onde são stateless e tais objetos devem ser obtidos da sessão
(HttpSession).
‡
Novamente, existem diversos outros métodos executados nesta fase, não representados. Note que a fase “beforePhase” para o
evento “RENDER_RESPONSE 6” é a de maior duração.
§
Esta JSP atua como um Servlet, sendo inclusive declarada como tal no “web.xml”, em uma das técnicas mais utilizadas para
integração os frameworks JSF e Tiles. Como internamente, toda JSP se torna um Servlet, neste caso ela é efetivamente utilizada como
um Servlet de Front Controller para poder ser valer de Tag-Libs Tiles para inserção de leiautes.
#7. Cada definição, em seu nível, pode declarar o uso de uma classe Java considerada de camada
Controle, chamada “Tiles Controller”*. Este tipo de classe implementa a interface “Controller”
do Tiles, que exige o método “execute”. Este método tem código que roda antes do despacho
de cada trecho de leiaute, permitindo programação dinâmica, em seu contexto.
#8. Cada definição, em seu nível, após a execução da classe Tiles Controller, despacha a execução
de uma JSP de leiaute (path) e outras adicionais representando componentes do segmento de
leiaute. As páginas JSP, por sua vez, podem conter diversos componentes Trinidad,
representados por Tag-Libs JSP.
*
Esta é uma classificação que pode ser polêmica. Por ocorrerem já na fase de renderização do documento de resposta, poder-se-ia
classificar as classes de controle do Tiles como de “camada Visão”. Porém, estas classes de controle Tiles são certamente
“controladoras” do ponto de vista do Tiles e podem ser entendidas como complementares as classes de Action, sendo inclusive
independentes do formato do documento – por este motivo, são organizadas como camada Controle, do ponto de vista do jCompany.
Figura E16.14. “AppPhaseListener” e “PlcPhaseListener”.
#1. As classes de implementação de “PhaseListener” são stateless, significando que não possuem
propriedades com estado variante (variáveis de instância).
#2. Os métodos requeridos pela interface “PhaseListener” estão disponíveis, para sobreposições
que se possam fazer necessárias.
#4. Diversos “Template Methods” estão disponíveis. Os métodos que terminam com “Antes”,
“Apos” e “Api”, como já vimos, são convenções que indicam que não possuem código no
ancestral – dedicados a especializações.
#5. O mecanismo de IoC que faz com que “AppPhaseListener” atue é provido pela implementação
JSF. Os templates INI trazem este descedente declarado no “faces-config.xml” para que atue e
facilite implementações específicas.
#1. As classes utilizadas como “Action” no JSF são “Statefull”, ao contrário do Struts, onde são
“Stateless”.
#3. A maior parte do código das classes “Service” da camada Controle são independentes da
tecnologia Struts ou JSF†. Quando há alguma diferenciação específica que necessite de
acoplamento a alguma destas tecnologias, uma classe de serviço descendente é definida
(geralmente para JSF), como as demarcadas, mantendo a independência do ancestral
genérico.
*
Uma das diferenciações do conceito de Delegação comparado a uma chamada simples é a flexibilidade para se especializar as classes
de serviço utilizada em ancestrais. No jCompany, é possível se declarar especializações destas classes em metadados, alterando
qualquer comportamento envolvido na agregação, não somente da classe principal.
†
Este é um bom exemplo de ganhos advindos da Delegação. Muito embora a classe Raiz seja dependente de tecnologia JSF ou Struts,
os códigos comuns a ambas estão fatorados nos serviços.
Figura E16.16. Agregação “PlcBaseJsfAction”, do ponto de vista de utilitários (Helper).
#1. Classes de utilitários utilizadas com freqüência por toda a classe, são definidas em variáveis de
instância, para maior clareza.
#2. Classes de utilitários são definidas com DP Singleton. Note que este DP exige construtores
“privados” para impedir instanciamentos desnecessários – que como efeito colateral também
impedem a herança. Deste modo, classes utilizada com DP Singleton do jCompany não
são projetadas para serem especializadas. Geralmente contêm métodos simples, que
podem ser substituídos inteiramente por outros, sem produzirem redundância significativa.
*
Exemplos são o “perfil do usuário corrente” e alguns metadados que podem ser modificados dinamicamente.
Figura E16.17. Agregação “PlcBaseJsfAction”, do ponto de vista de metadados.
#1. Os objetos que contêm representações de metadados para uma Colaboração corrente são
injetados como variáveis de instância no “PlcBaseJsfAction” pelo JBoss Seam.
#1. Os objetos que representam agregações de Domínio e também o POJO que encapsula
informações de contexto* também ficam disponíveis para a classe “PlcBaseJSFAction” como
variáveis de instância, e são gerenciados pelo JBoss Seam.
É importante notar que estes objetos têm importância própria e fazem sentido mesmo que
desvinculados do “PlcBaseJsfAction”. Portanto, não são considerados “agregações” da classe
“PlcBaseJsfAction”.
...
<plcf:iteracao id="plcLogicaItens" value="#{plcLogicaItens.itensPlc}">
(...)
<plcf:celula>
<plcf:texto id="nome" value="#{item.nome}" ajudaChave="ajuda.nome" />
</plcf:celula>
...
Código E16.15. Referência a “itensPlc”, dentro de “plcLogicaItens” (o “Action” corrente é assumido
implicitamente pelos componentes JSF do jCompany).
O componente “iteracao" cria um laço e disponibiliza cada objeto da coleção na variável local
“item”, de modo que todo o “grafo de Domínio” pode ser acessado via notação de pontos tipo
“#{item.objeto.[objeto]}”.
*
Na prática, dados mantidos na sessão HTTP ou em uma conversação JBoss Seam – de interesse dos serviços da camada Modelo.
#3. Note que a coleção anterior pode não ser mantida durante uma conversação*, o que é inclusive
o default do jCompany para evitar aumento de consumo de memória sem necessidade. O uso
do metadado de controle “detalheLembra=true”, para a Colaboração, faz com que coleções
previamente recuperadas (portanto, antes de eventuais modificações) também sejam mantidos
em memória durante uma conversação.
...
comportamento = @PlcConfigComportamento(detalheLembra = true),
...
Código E16.16. Indica para o framework manter a coleção ‘anteriormente recuperada’ em
“plcLogicaItensAnterior”.
Esta configuração somente deve ser usada quando for necessário implementar lógicas de
programação do tipo “se alterou uma propriedade do valor ‘A’ para o valor ‘B’, então
...”.
#4. Se o padrão da Colaboração for “plcCrud”, “plcMestreDetalhe”, “plcMantemDetalhe”,
“plcManSubDetalhe”, “plcPrefAplicacao” ou “plcPrefUsuario”, ou seja, em padrões onde
se manipula “uma agregação por vez”, então a agregação de domínio é incluída diretamente
em “plcEntidade”, através de referência ao seu objeto Raiz. Neste caso, a versão anterior é
sempre mantida pelo jCompany por default, mas somente do objeto Raiz da agregação.
#5. Além do modelo de Domínio, em seus estados atuais e anteriores a modificações, um POJO
adicional é sempre enviado pelo jCompany, da camada Controle para a camada Modelo,
conforme pode ser investigado pela assinatura do Façade "IPlcFacade". Este objeto
implementa o DP J2EE Context Object, que define padrões para encapsulamento de dados
úteis aos serviços de Modelo, porém mantido somente no contexto da camada Controle†.
*
Indicado pela multiplicidade zero no diagrama UML.
†
Muito embora este objeto seja relativamente extenso, a maior parte das informações não é composta em uma requisição típica - o
envio do perfil do usuário corrente é seu propósito mais fundamental, na maior parte dos casos.
Figura E16.19. Esquema previsto para especialização dos serviços centrais de controle.
#1. A classe “PlcBaseJsfAction” contém as implementações específicas para cada evento padrão
(disparados, em sua maioria, pelos botões de ação), utilizando DP Template Method. Desta
vez, representamos apenas a parte de operações desta classe, para enfatizar esta
implementação (nos outros diagramas, analisávamos as variáveis de instância).
#2. Uma classe “AppAction” é configurada como default, em cada projeto, através dos templates
INI, pronta para conter especializações genéricas para toda a aplicação. No exemplo acima,
esta classe sobrepõe um método exclui para toda a aplicação (algo mais “radical”, porém
possível) e também acrescenta procedimentos após a edição, para todos os objetos.
#5. Para especializar serviços que recebem delegações da classe “PlcBaseJsfAction”, tais como
"PlcValidacaoJsfService", basta criar um descendente, declará-lo como uma classe
gerenciada pelo Seam e implementar um novo método de “factory”, em “AppServiceFactory”,
classe disponível nos templates INI. Exemplos:
@Name("AppMeuValidationJsfService ")
@Scope(ScopeType.APPLICATION)
public class AppMeuValidationJsfService extends PlcValidacaoJsfService{
@Name("AppServiceFactory")
@Scope(ScopeType.APPLICATION)
@AutoCreate
public class AppServiceFactory {
@Factory(value="serviceValidacaoJsfPlc")
public PlcEntidadeService geraValidacaoJsfService() {
return (PlcValidacaoJsfService)Component.getInstance("AppMeuValidationJsfService",true);
}
}
#6. Finalmente, para repassar novas informações de contexto para a camada Modelo, deve-se
especializar o POJO “PlcBaseContextVO”, criar a nova informação de contexto (por exemplo,
empresa ou filial corrente, em uma aplicação multi-empresa) e, em seguida, especializar a
classe Action para montar este valor.
2. Antes de editar estas classes, experimente alterar a URL, incluindo três diferentes níveis de detalhe
via parâmetro “logSpyPlc=”, informando valores de 1 a 3. Em cada nível, um maior número de
classes é exibido. O resultado do nível 1 é exibido na Figura E16.21.
Figura E16.21. Todas as classes de log que possuem pacotes padrões do jCompany.
Obs.: O nível 3 filtra somente classes de logging de JSPs pré-compiladas, que aparecem após uma
“liberação com pré-compilação”, voltada para produção. Por este motivo, em desenvolvimento
pode-se não perceber diferenças significativas entre os níveis 2 e 3.
3. Agora retorne ao Eclipse e procure a janela de console do Tomcat. Clique no botão “Clear Console”
exibido na Figura E16.22.
Figura E16.22. Console do Tomcat no Eclipse. Somente SQLs são exibidos, na configuração padrão.
Dois problemas podem acontecer aqui: se a janela “Console” não estiver visível, você poderá ter
que ativá-la através da opção de menu “Windows -> Show View”; ou se a janela console estiver
exibindo informações do Apache Derby ou Maven, você terá que comutá-la para Tomcat, no botão
“Display Selected Console”.
4. Volte o jCompany Log4j Console para o modo resumido, simplesmente informando “logSpyPlc=0”
na URL. Em seguida, troque o nível da classe de log “jcompany.controle.log” de “INFO” para
“DEBUG” e clique em “Set”.
Figura E16.23. Alteração dinâmica de nível de logging para classe especial do jCompany.
5. Agora retorne para a aplicação e acesse a URL “/f/t/uf”. Após sua execução, comute para o Eclipse e
consulte o resultado na janela de Console. Ele deve estar semelhante ao resultado exibido na Figura
E16.24.
Figura E16.24. Perfil de execução via logging, destacando classes/métodos fundamentais da camada Controle.
Veja como agora fica fácil identificar a seqüência de empilhamento e desempilhamento das
execuções, através da endentação automatizada que o jCompany provê para estas classes
especiais de logging, nomeadas como “com.powerlogic.jcompany.[camada].log”.
Este recurso, aliado com o bom uso do logging convencional, pode auxiliar na identificação de
gargalos de performance e também na localização de problemas, de forma complementar ao
depurador.
6. Experimente agora ligar também o nível de DEBUG para a classe
“com.powerlogic.jcompany.visao.log”, para ver o fluxo de camada Visão, juntamente com o da
camada Controle. Estude o resultado juntamente com os diagramas de seqüência que estudamos
anteriormente.
*
Em Struts, o jCompany implementa uma solução chamada de “one-click profiling”, que exibe uma pilha de execução também via
Log4j, mas utilizando técnicas de AOP via CGLib, para exibir métodos do Tiles e Struts, dentre outros. Esta é ainda uma visão
intermediária entre o log que vimos para JSF e um profiling completo.
Nos cenários eventuais quando necessitarmos de conhecimento ainda mais profundo, a recomendação é
para usarmos as ferramentas de depuração (Debug) do Eclipse, o que pode ser feito com os seguintes
passos:
1. Após descobrir um trecho de execução “suspeito”, seja na aplicação, seja no jCompany ou
frameworks de base, navegue via hiperlinks pelos códigos fontes destas classes (“control+Clique”
sobre o nome das referências) e aperte “duplo-Clique” na barra lateral esquerda da linha desejada,
para marcar um ponto de parada.
Figura E16.25. Linha de código de classe do jCompany, com “breakpoint” para depuração.
2. Ao executar a aplicação novamente, o “Debug” do Eclipse irá propiciar os diversos recursos que se
espera dos melhores utilitários para este fim, tais como execução “passo a passo”, investigação e
alteração dos valores de variáveis na memória, inclusão de condicionais (Ex.: “somente parar aqui
se variável a for igual b”) e inclusive alteração de código com efeito imediato – seguida de um “Hot
Deploy”.
#1. Relação de “Threads” em execução no Tomcat. Como trabalhamos com ambientes de execução
multi-thread, existirá um conjunto delas (pool) criadas na memória, mas apenas uma conterá a
nossa execução principal, e esta já se apresentará aberta, com a pilha de execução real
exibida, até o ponto de parada definido.
#2. Uma barra de botões permite opções tais como execução “linha a linha” (botão verde), salto
para o próximo ponto de parada, retrocesso e indicativo para se entrar em uma função (em
lugar de considerá-la apenas uma linha de execução).
#3. Na janela “Variables”, pode-se consultar e alterar valores de variáveis em memória, na medida
em que se avança de forma paulatinamente, na execução.
#4. Em “Breakpoints”, podem-se excluir pontos de paradas existentes ou incluir novos, na medida
em que se executa a depuração, inclusive informando-se condicionais.
#5. Finalmente, com o código fonte disponível, pode-se alterá-lo, para avaliar seu efeito imediato.
Lembre-se de que este “Hot-Deploy” realizado não é perene, e será perdido após um reinício
do Application Server. Após algumas alterações em modo de depuração, deve-se realizar uma
liberação “rápida com reinicio”.
Para aprender mais sobre a opção de “Debug” do Eclipse, consulte no Ajuda On-Line do Eclipse o tópico
“Java Development User Guide -> Concepts -> Debugger”. Se você não conhece ainda esta ferramenta,
esta é uma leitura obrigatória!
Programações da Camada Modelo - Fachada
- Visão geral
O estudo da programação da camada Modelo pode ser segmentado em duas subcamadas, já
desconsiderando a de Persistência:
Subcamada de Fachada (DP Façade): Realiza a adaptação de requisições da camada Controle para a
subcamada de Serviços ou do Negócio, incluindo o delineamento das transações. Não implementa os
serviços em si, mas delega para a subcamada de Serviços. Esporadicamente, para casos mais
simples, pode chamar a camada de Persistência diretamente*.
Subcamada de Serviços do Negócio (DP Java EE AS-Application Service ou BO-Business
Object): Realiza os serviços principais do negócio, via delegação das implementações da fachada. É
importante esta separação, para manter os métodos de serviços com assinaturas “de negócio”. Além
disso, por não delinearem transações, os métodos desta camada se tornam mais reutilizáveis.
*
A chamada de serviços da persistência diretamente de implementações de fachada é aceita pelos DPs J2EE, para casos triviais, mas
pouco utilizada pelo jCompany, exceto em recuperações de listas simples para colaborações “plcTabular”. A rigor, esta chamada direta
indicaria que a subcamada de fachada e a subcamada de serviços são laterais mas, na prática, é melhor compreendê-las como
camadas superpostas.
†
Nesta arquitetura, um arquivo EAR contendo arquivos JAR gerados dos projetos “rhtutorial_modelo” e “rhtutorial_comuns” deverá ser
embalado e instalado para funcionamento remoto, em um container EJB3, com seus serviços acessíveis via RMI/IIOP, JMS ou Web-
Services. Já os projetos “rhtutorial (principal)” e “rhtutorial_comuns” (este último residirá nas duas pontas, possivelmente, ao menos
para os Casos de Uso Padrões), devem sem embalados um arquivo WAR, funcionando como “cliente”. Esta arquitetura será analisada
no livro “Volume II – Tópicos Avançados”, desta sérieo.
o jCompany já disponibiliza inclusive a interface "IAppFacade" e "AppFacadeImpl", nas aplicações,
atuando prioritariamente através de um mecanismo de IoC próprio. Veja na Figura E16.27.
#1. Contrato de Fachada Padrão para Manutenção de Ciclo de Vida e Consulta Parametrizada, do
jCompany.
#3. A implementação possui ancestrais com métodos de apoio restritos a esta subcamada.
#5. Descendente do Contrato de Fachada Padrão, na aplicação, para permitir novas “cláusulas
contratuais”, que tenham afinidade com o assunto do contrato.
#6. Versão da interface remota na aplicação. Veja que aqui utilizamos “herança múltipla”, possível
para Interfaces (esta Interface especializa tanto o "IAppFacade" quanto o "IPlcFacadeRemote")
#7. Descendente da Implementação de Fachada Padrão, para implementações padrões das novas
“cláusulas contratuais”.
É importante notar que o uso do esquema padrão de especialização do contrato de fachada deve estar
estritamente relacionado ao seu objetivo (“objeto principal” do contrato), que é a Manutenção e
Consulta Parametrizada em Agregações de Entidades. Para quaisquer outros casos de serviços
necessários, mas não relacionados ao objeto, deve-se definir um contrato e implementações de
fachada “à parte”.
Praticaremos com estas duas situações, em tutoriais dos próximos capítulos.
Figura E16.28. Seqüência em maior profundidade, da arquitetura de IoC utilizada para localizar a fachada.
#1. Métodos de controle tais como o “pesquisa” obtém referências à Interface de fachada
“IAppFacade”, através do método “getServiceFacade()”, que encapsula toda a
complexidade envolvida em sua obtenção.
#4. Quando utilizando tecnologia EJB3, esta classe irá delegar a localização da referência para a
classe "PlcEjbHelper", que encapsula o algoritmo de DP Business Delegate, para obter uma
referência possivelmente remota da fachada*.
*
Perceba que, muito embora neste cenário o “AppFacadeImpl” seja um EJB3 Session Bean, ele necessariamente poderá não estar
remoto, sendo também possível acessá-lo localmente, segundo o padrão EJB.
#5. Quando utilizando POJO será utilizado um algoritmo de IoC via DP “Service Locator” e reflexão,
para pegar uma referência de implementação localmente disponível (camada Modelo no
mesmo WAR).
#6. O retorno de cada um dos métodos irá variar, sendo a Interface potencialmente remota
devolvida no primeiro caso, e a local no segundo. Mas em qualquer dos casos, o uso funcional
permanecerá idêntico.
#7. No primeiro caso a classe de controle, agora de posse de uma referência de Interface remota,
indiretamente pode disparar chamadas remotas, no padrão EJB3.
#8. No segundo caso a classe de controle realiza sempre uma chamada local, indiretamente via
Interface. Note que, indiferentemente da forma e tecnologia de implementação, a
camada Controle opera da mesma forma, nos dois cenários!
Figura E16.29. Seqüência em maior profundidade, da arquitetura de IoC utilizada pela fachada.
#1. A maior parte dos métodos de acionamento são como os iniciados por esta primeira seqüência,
típica da pesquisa quando feita em colaborações “plcTabular” ou “plcConsulta”.
#2. A implementação de Fachada irá realizar procedimentos mínimos e pegar uma referência da
classe de utilitário para localização serviços, através do método “getBO(PlcBaseVO)“ que, a
exemplo do “getServiceFacade”, irá encapsular a lógica de obtenção do serviço de Business
Object apropriado para tratar a requisição.
#4. O algoritmo de localização do melhor serviço, por si, é delegado para a classe
“PlcModeloIoCFactoryService”, que é extensível. Este algoritmo recebe uma classe de
Entidade e procura localizar uma classe de gerenciamento com base em convenção de
nomenclatura (Ex.: "FuncionarioManager" para entidade "FuncionarioEntity") ou com
base em anotações explicitas, na Entidade (Ex.:
@PlcIoC(nomeClasseBC=”com.empresa.rhtutorial.modelo.MeuServico”)). Se não
encontrar, reutiliza uma instância da classe padrão “PlcBaseBO”.
#5. A partir da segunda chamada, a referência que está em cache para a requisição é reutilizada.
#7. A classe “PlcPersistenciaLocator” executa algoritmo similar ao das classes anteriores para
Modelo e Facade, incluindo a delegação do algoritmo de IoC para a classe
"PlcPersistenciaIoCFactoryService", extensível.
#9. A referência de persistência é então obtida para uso direto do método de fachada
(recomendado somente para casos mais simples, como este).
#1. Como o “Context Object” é mais utilizado em manutenções, desta vez vamos analisar a
seqüência de transação do ponto de vista de um método de atualização como o “grava”.
Por default, somente informações que o jCompany necessita são montadas, mas novas
informações de contexto podem ser adicionadas via especializações. Importante: este objeto,
quando especializado, deve conter somente metadados e informações de contexto! Parâmetros
“do negócio” devem ser explicitamente enviados como tais, explícitos em novas cláusulas do
contrato de fachada (“IAppFacade” ou outro).
Após sua criação, que ocorre uma vez a cada requisição, o objeto de contexto é armazenado
com escopo de requisição (HttpServletRequest), evitando de ser passado também na
assinatura dos métodos de controle, para não “poluir” todas as assinaturas.
#3. Antes de enviar chamadas à fachada, o métodos de controle obtêm uma instância atual do
objeto de contexto através de “getContext”. Este método recupera do escopo de requisição o
objeto de contexto gerado.
#4. Nas chamadas de fachada, como reza o DP Context Object, este objeto é enviado
explicitamente (Lembre-se de que esta pode ser uma chamada remota). Além disso, como há
restrição ao uso da tecnologia de Servlet na camada de modelo, mesmo com as camadas
embaladas localmente (no mesmo WAR), não haveria como a camada Modelo obter este
objeto.
*
Lembrando que aqui também atua um esquema de IoC. Ou seja, é possível declarar um outro descendente específico de
“PlcBaseContextVO”, para conter valores de “contexto” específicos do negócio, para sempre ser enviado para serviços da camada
Modelo.
#6. Este trabalho é então delegado para uma classe de gerenciamento de Context Objects
chamada “PlcContextManager”, que armazena o objeto em uma “Thread Local”*.
#7. A “Thread Local” funciona como área globalmente visível durante a seqüência de
processamento da camada modelo, no escopo da requisição, como desejamos. A classe
“PlcContextManager” encapsula uma declaração estática com esta “Thread”.
#8. Métodos da camada de serviço (ou do negócio) não mais necessitam enviar este objeto como
parâmetro, ficando com a assinatura mais sucinta e objetiva, definindo estritamente a sua
necessidade de negócio.
#11. Finalmente, quando se trabalha com Hibernate, seja diretamente, seja como uma
implementação JPA, o jCompany possui interceptadores (Hibernate Listeners) em eventos de
persistência que visam realizar otimizações (evitar queries desnecessárias em diversos
momentos) e também registrar usuário e data da última alteração, para auditoria “pauta
mínima”.
Nesta classe, o objeto de contexto é utilizado para se capturar o padrão corrente (para
otimizações), nomes de classes de Raiz/Mestre e Detalhe – além do perfil do usuário corrente.
*
Para compreender melhor o que são as Thread Locals e a técnica de algoritmo empregada nesta área, sugerimos a busca por este
termo em documentações da Sun e listas em geral. O algoritmo utilizado pelo jCompany é bastante comum sendo inclusive, o mesmo
utilizado internamente pela Hibernate, para manter referência às sessões de persistência.
Porém, um grande leque de requisitos não exigirá estas sofisticações, e nestes casos o uso do
JTA não trará vantagens consideráveis. Pelo contrário, irá gerar complexidade para uma taxa de
retorno próxima de zero!
Note que benefícios tais como “transação declarativa via anotações/AOP”, providos adicionalmente ao JTA
pela tecnologia EJB3, estão também disponíveis no jCompany com POJOs, bem como IoC e DI. Como
uma eventual evolução de POJO para EJB3 será possível, quando, e se, for necessária – o recomendável
para a ser a utilização da alternativa “mais simples que atenda aos requisitos em jogo”, em geral a
transação local via JDBC.
Vamos analisar os diagramas das Figura E16.31 e Figura E16.32, que explicam a arquitetura de
gerenciamento de transações locais declarativas via AOP, do jCompany.
Figura E16.31. Estrutura das classes envolvidas no AOP para fachadas, utilizando CGLib.
#1. O framework “CGlib” provê AOP (Aspect Oriented Programming), permitindo que instanciemos
classes descendentes (de quaisquer outras) que somente existem em tempo de execução,
conhecidas como “proxies”. O jCompany traz uma classe "PlcAopManager", na camada
“comuns”, que encapsula este processo de instanciação de proxies CGlib via a chamada:
PlcAopManager.createProxy(classe,aopBaseCallback);
onde
- classe: é a classe AppFacadeImpl (ou qualquer outra de fachada, em nosso caso)
- aopBaseCallback: É a classe que implementará métodos de interceptação dos métodos de
‘classe’.
#2. A classe “Proxy”, após ser criada em tempo de execução, funciona como um descendente e
tem sufixo padrão assumido dinamicamente pelo CGLib como “$$EnhancerByCGLib$$”.
#1. Ao capturar uma interface de fachada, em tempo de execução, vimos que a implementação
retornada é instanciada via CGLib, sendo na verdade um “Proxy” (descendente dinâmico) da
classe “conhecida” pelo desenvolvedor.
#2. A classe de “Proxy” tem nome igual ao da classe base, sufixada com “$$EnhancedCGlib$$”.
A qualquer tentativa de executar métodos de fachada, em “AppFacadeImpl”, ela irá capturar
a tentativa e passar para a classe de interceptação, “PlcAopModeloCallbackService”.
#3. A classe de interceptação (na verdade, como vimos, seu ancestral), chama o método
“interceptaAntes”, mas não há implementação corrente do jCompany neste ponto.
#4. Em seguida, ela chama o método original, acionando diretamente o “ancestral” do “Proxy”, via
comando: “retValFromSuper = proxy.invokeSuper(obj, args);”.
#5. A seqüência então toma seu rumo original, com chamadas possíveis à camada Modelo e
Persistência.
#6. À primeira tentativa de se pegar uma sessão de persistência, seja via Hibernate (Session) ou
JPA (EntityManager), uma nova transação será iniciada, com o SGBD (BEGIN TRANSACTION).
#8. O comando JPA difere ligeiramente do comando Hibernate, para abertura de transação, mas o
jCompany os torna transparentes do ponto de vista do desenvolvedor.
#9. Após a execução do método interceptado, o interceptador chama o método “interceptaApos”.
Este sim, é encarregado de realizar o fechamento das transações, em conformidade com as
anotações e o transcorrer da requisição sem exceções, conforme já foi explicado.
#10. Note que, quando programa em pontos de extensão “[metodo]Apos” dos Template Methods da
camada Modelo, o desenvolvedor está codificando “dentro da transação”, significando que
qualquer objeto que venha a ser persistido manualmente, neste ponto, será gravado
na mesma “Unidade Lógica de Transação – ULT” da agregação principal gravada,
genericamente.
*
Envolve a captura de uma conexão JDBC de um conjunto (pool) mantido pelo App Server para uso pela implementação de
persistência e sua devida liberação, após o uso, de forma transparente para o Desenvolvedor.
assim, permite intervenção e gerenciamento manuais assistidos (semi-automatizados), tipicamente para
atendimento a transações BATCH, que exigem gerenciamento diferenciado*.
...
public interface IAppFacade extends IPlcFacade {
...
public class AppFacadeImpl extends PlcFacadeImpl implements IAppFacade {
@PlcTransacaoLeitura
public Integer calculaTotalCandidatosPorSexo(String sexo) throws PlcException {
Se desejarmos que este serviço seja visível para reúso em outras aplicações, devemos criar um novo
contrato, digamos “IFuncionarioService” (leia-se: serviços associados a funcionários – explicitando a
intenção de reúso corporativo), e disponibilizá-lo em um “módulo”, que no jCompany segue padrões
bem definidos: projeto Eclipse distinto, empacotado em JAR separado, etc.. Os trechos de Código E16.21
e Código E16.22 exemplificam esta opção.
...
public interface IFuncionarioService {
...
public class FuncionarioServiceImpl {
@PlcTransacaoLeitura
public Integer calculaTotalFuncionariosPorSexo(Sexo sexo) throws PlcException {
Neste segundo caso, para instanciação simplificada, é recomendável que se crie a Interface e a Classe de
implementação com padrões de nomenclatura: “I[Nome]” e “[Nome]Impl”, respectivamente. Se
*
O gerenciamento de transações “semi-automatizado”, para casos de programação BATCH será exemplificado em tutorial nos
próximos capítulos.
†
Este contrato de fachada deve ser entendido como um “contrato de fachada para a aplicação corrente”, para operações que não
sejam projetadas para reúso corporativo.
ambas estiverem no mesmo pacote* e seguindo o padrão de nomenclatura acima, podem-se obter
referências corretamente instanciadas (ou seja, como Proxies com transação gerenciada) a partir das
classes de Action, conforme o trecho de Código E16.23.
...
public class MeuAction {
*
Note que, por “mesmo pacote”, não queremos dizer que estarão no “mesmo projeto” ou nem na “mesma camada MVC”. Ex.: A
Interface “IPlcFacade” do jCompany, está no pacote “com.powerlogic.jcompany.facade”, assim como a implementação “PlcFacadeImpl”.
Mas a Interface fica no projeto Eclipse “comuns” (camada ortogonal, visível por todas as demais do MVC2-P), enquando a Classe fica no
projeto Eclipse “modelo” (em escopo de camada Modelo).
Programações da Camada Modelo – Serviços de Negócio
Ainda com relação à nomenclatura, é recomendável que se utilizem convenções de sufixo diferentes, para
ênfases arquiteturais diferentes:
BO + VO: O uso de BO como sufixo para classes de serviço da camada Modelo remete aos padrões de
nomenclatura clássicos do J2EE, normalmente utilizados em conjunto com o sufixo VO (Value
Object) para o que seriam nossas Entidades (para as quais temos utilizado Entity). Neste contexto
“J2EE Clássico”, os BOs absorvem todo e qualquer processamento, deixando para as “Entidades do
tipo VO” somente a responsabilidade de transportar dados (similar aos DTOs).
Apesar de ainda ser possível se trabalhar deste modo no jCompany*, recomendamos a abordagem
Domain-Driven Design (DDD), mais interessante e Orientada a Objeto.
Manager + Entity: Para uma ênfase maior em OO via práticas de Domain-Driven Design (DDD), nossas
próprias Entidades encapsularão métodos que representam regras de negócio de sua alçada. Os
antigos BOs, agora sufixados como “Managers”, irão apenas recepcionar eventos e, eventualmente,
tratar pré-condições e acionar serviços de dados. Em se havendo regras de Domínio, estas serão
delegadas para as Entidades responsáveis.
Importante: Feita a recomendação, é importante frisar que o jCompany não impedirá que se trabalhe
com este ou aquele sufixo. Qualquer outro poderá ser definido em metadados, no escopo de aplicação -
recomenda-se apenas que seja usado um mesmo padrão, para uma mesma organização.
Um exemplo de implementação deste tipo de classe pode ser visto no trecho de Código E16.24.
...
public class FuncionarioManager extends PlcBaseBO {
// 1
FuncionarioDAO funcDAO;
public FuncionarioManager(FuncionarioDAO funcDAO) {
this.funcDAO = funcDAO;
*
Inclusive, em seus ancestrais, o jCompany FS Framework ainda usa estes sufixos, em “PlcBaseVO” e “PlcBaseBO” para Entities e
Managers, respectivamente, por questão de compatibilidade.
}
// 2
protected void incluiAntes (PlcBaseVO entidade) throws PlcException {
// 3
Funcionario funcionario = (Funcionario) entidade;
// 4
FichaFuncional ff = funcDAO.recuperaFicha(funcionario.getCpf());
// 5
funcionario.validaMaximoSalario(ff);
}
}
Código E16.24. Classe Manager acessando serviço de dados em DAO e delegando regras para Entidades de
Domínio.
#3. A primeira providência ao especializar Template Methods, é fazer ajuste de tipo (casting) das
Entidades recebidas – se possível, tentando trabalhar com o ancestral abstrato, que encapsula
as informações de negócio (“Funcionario”, e não “FuncionarioEntity”).
#4. Acesso ao serviço de dados. Somente classes de Facade, AS ou BO/Managers podem acessar
DAOs.
#5. Delegação para que a Entidade Funcionário implemente a regra de negócio em si. Ela não foi
representada, por não ser relevante, mas perceba que é de responsabilidade da camada de
Domínio. Os métodos de serviços do tipo “Manager” irão realizar interceptação de eventos,
acionamento de serviços de dados, tratamentos eventuais de pré-condições e outras atividades
de “burocráticas” do serviço, mas delegarão para Entidades de Domínio regras de sua alçada.
#2. Um ancestral "AppManager" é gerado via template INI por default, como sugestão para
conter extensões para toda a aplicação (e, neste caso, já faz uma “adaptação de nome” para o
sufixo “Manager”). No exemplo, um código foi incluído para executar após a inclusão de
qualquer agregação, mantida dentro desta aplicação.
Note o grande número de dependências desta classe. Ela irá depender de classe de DAO para
serviços de persistência e do modelo de Domínio, para regras de negócio, no mínimo.
#4. Para enfatizar o desacoplamento com a camada DAO, o recomendado é utilizá-la via Interfaces,
mantendo uma fachada (DP Façade) também entre a camada Modelo e a de Persistência.
Deste modo, facilitam-se futuras portabilidades.
#5. Mas é possível utilizar a implementação de DAO diretamente, sem grandes prejuízos na maioria
dos casos. Neste caso, mantém-se o isolamento entre as camadas Modelo e Persistência, mas
sem o rigor de outra “fachada”. Muito embora o jCompany utilize Interfaces, a extensão de
seu uso pelo desenvolvedor é opcional, em função das perspectivas do cliente de manutenções
nesta área. Em ambos os casos, será possível a utilização de DI nas classes Manager.
Importante: Ao escolher uma das duas abordagens, deve-se manter fiel a ela,
preferencialmente em nível da empresa.
#1. A maior parte das implementações de classes Manager serão interceptações de Template
Methods, que mantém o ciclo de vida de agregações, na camada Modelo.
#2. As implementações principais dos métodos chamam métodos vazios para especialização,
injetando uma instância da agregação a ser mantida, como parâmetro*.
#3. Métodos de extensão serão possivelmente especializados para executar acessos a dados e
tratar pré-condições.
#4. Após recuperar os dados necessários, de forma a prover valores para as “variáveis” a serem
utilizadas em regras de negócio, a classe Manager delega esta incumbência para Entidades de
Domínio. Regras que não sejam claramente encasuláveis, podem ser agrupadas em classes
próprias, transientes, também na camada de Domínio.
#5. A classe de Manager somente aciona regras a partir da classe Raiz da Agregação. Esta, por sua
vez, pode trocar mensagens dentro de seu grafo de alcance, de modo a complementar as
informações necessárias para o resultado esperado.
Existem três regras práticas que devem orientar um bom projeto de classe de AS:
A classe de Application Service, em si, não deve implementar cálculos do negócio, mas
somente fluxos de delegações, incluindo condições e laços. É o conceito de orquestração,
propriamente dito, em nível do Caso de Uso†.
Na dúvida sobre implementar em Fachada ou em AS, prefira AS. Haverá uma natural tentação de
ser fazer, na própria implementação de fachada, toda a orquestração de chamadas necessária, mas
haverá algumas desvantagens: para casos complexos, isso impede o reúso de blocos de
processamento de AS, já que estes ficam acoplados a uma transação; não é possível usar
“Constructor Based Dependency Injection” no Façade; algumas tarefas de adaptação da fachada
podem “poluir” as orquestrações de negócio.
Na dúvida sobre implementar em Manager ou AS, pense nos “imports”: Por ser uma
orquestradora, a classe de AS tenderá, naturalmente, a possuir dependências com um grande
número de classes (em conformidade com o tamanho e extensão de seu algoritmo). Pense: Se estas
importações fossem realizadas no Manager, elas trariam acoplamento com classes que não têm
relação com a agregação gerenciada por esta última classe? Por exemplo, mover importações de
“TabelaIR” para “FuncionarioManager”, sendo a primeira uma classe que não pertence ao grafo de
“Funcionario”, é um sintoma de que métodos apropriados para AS podem estar sendo movidos para
Managers, indevidamente.
Implementações de AS devem herdar de “PlcBaseAS” para obterem recursos de caching, IoC e DI. Um
exemplo de implementação pode ser visto no trecho de Código E16.25.
...
public class CalculoFolhaAS extends PlcBaseAS {
...
// Injeção de Dependência no construtor
....
public Integer calculaFolha(Periodo periodo) throws PlcException {
(...)
*
O método de alteração traz ainda a imagem anterior da agregação também (antes de sofrer modificações), porém somente com a
imagem anterior do seu objeto “raiz” (ex.: Funcionario). Para que “agregados” anteriores sejam também “lembrados”, deve-se
informar o metadado “detalheLembra=true”.
†
Em uma analogia com ferramentas de workflow ou ferramentas de BPM/SOA, esta classe representa de forma programada, o “fluxo
de trabalho” propriamente dito, mas não necessariamente as regras disparadas durante o fluxo.
}
}
…
Código E16.25. Classe de Application Service. Nada de regras de negócio – somente chamadas, laços e
condicionais.
#1. Application Services são provavelmente acionados por novos eventos, e não a partir dos
eventos padrões do jCompany.
#2. A implementação de fachada não orquestra a execução. Ela apenas realiza ajustes em
argumentos e delineia a transação*.
#3. O Application Server executa uma varredura por todos os funcionários para os quais deverá ser
executado o cálculo, para o período passado por parâmetro.
#4. Operações de ciclo de vida são utilizadas via “FuncionarioManager”, tais como a recuperação de
um funcionário. Os métodos genéricos são utilizados, desde que complementações ao ciclo de
vida sejam relevantes. Pode-se também utilizar o "FuncionarioDAO" diretamente, exceto se a
empresa quiser estabelecer o rigor de sempre haver “Managers” intermediando (como uma
camada, entre os “Application Servers” e os “Data Access Objects”).
#5. Rotinas de orquestração podem ficam extensas. Deve-se buscar uma segmentação que facilite
a compreensão, preservando a simplicidade do fluxo de execução tanto quanto possível, em
cada método do AS.
#6. Chamada direta de classe DAO, para requerimentos “centrados em dados”, basicamente.
#7. Chamada direta a Entidade. A camada de Domínio é ortogonal, e visível por todas as demais.
*
Neste exemplo, pouco provável no mundo real, estaríamos encerrando um cálculo de folha com apenas um “commit()” ao final! O
mais provável seria termos de gerenciar transações manualmente, chamando os métodos “commit” ou “rollback” da interface DAO,
para gravar ULT menores, em blocos de 100 em 100 funcionários, por exemplo. Faremos um exemplo deste gerenciamento nos
próximos capítulos.
Importante: Classes de AS podem acionar quaisquer das classes de Entidade, Manager ou
DAO. Deste modo, elas ficam altamente dependentes de um grande número de classes, mas
têm esta complexidade balanceada com a simplicidade de não processarem regras de negócio,
internamente.
#8. Para a atualização, o acesso também é feito via Manager, caso os métodos de especialização ao
ciclo de vida sejam relevantes.
Programações da Camada Persistência
#1. A primeira classe a ser acionada, para devolver uma sessão de persistência, será a
"PlcHibernateManagerLocator", que é um singleton que armazena em caching as
referências aos objetos gerenciadores de sessões de persistência.
O arquivo de configuração define parâmetros globais para uma fábrica, tais como endereço
JNDI para pool de conexões e muitos outros, sendo alguns de maior relevância específica para
o jCompany, discutidos nos itens 5 e 6.
…
<property name="plc.manyToOneLazyOtimiza">S</property>
<property name="plc.updateOtimiza">S</property>
<property name="plc.autoDetectDialect">S</property>
<property name="plc.auditoriaRigida">S</property>
…
Código E16.27. Parâmetros de configuração especiais do jCompany no “hibernate.cfg.xml”.
Não é necessário nenhuma código Java específico nestas classes, a menos que se queira
modificar alguma programação genérica do jCompany, presente em
“PlcBaseHibernateManager”.
#8. Para cada caso exemplificado acima, deve-se definir um arquivo de configurações a exemplo do
“hibernate.cfg.xml”, com nomes respectivos “pedido.cfg.xml” e “corporativo.cfg.xml”. É
dentro destes que se pode modificar informações específicas, tais como dialeto (SGBD-R),
endereço JNDI do pool de conexões (que o jCompany irá utilizar), etc..
- Auto-Detect Dialect.
O jCompany possui a facilidade de detectar automaticamente o dialeto ideal para o SGBD-R corrente,
sem que o Desenvolvedor precise se preocupar em informá-lo nos arquivos de configuração. Este recurso
é útil para quem deseja fazer produtos portáveis para vários SGBDs e que, desta forma, não
precisam modificar em nada os arquivo WAR ou EAR finais!
A detecção automática investiga e decide qual dialeto utilizar, se baseando na URL declarada para a
conexão pelo driver JDBC, capturada via pool de conexões. Este processamento é delegado da
classe “PlcBaseHibernateManager” para a classe “PlcHibernateHelper”, que executa um código
similar ao Código E16.28.
…
if (url != null && url.toLowerCase().indexOf("derby")>-1) { // CloudScape/Derby
p.put("dialect", DerbyDialect.class.getName());
p.put("hibernate.dialect", DerbyDialect.class.getName());
} else if (driver.toLowerCase().trim().indexOf("ibm db2") > -1) { // DB2
p.put("dialect", DB2Dialect.class.getName());
p.put("hibernate.dialect", DB2Dialect.class.getName());
} else if (driver.toLowerCase().trim().indexOf("oracle") > -1) { // Oracle
p.put("dialect", Oracle9Dialect.class.getName());
p.put("hibernate.dialect", Oracle9Dialect.class.getName());
} else if (driver.toLowerCase().trim().indexOf("sqlserver") > -1){ //SqlServer
p.put("dialect", SQLServerDialect.class.getName());
p.put("hibernate.dialect", SQLServerDialect.class.getName());
} else if (driver.toLowerCase().trim().indexOf("mysql") > -1) { // Mysql
p.put("dialect", MySQLDialect.class.getName());
p.put("hibernate.dialect", MySQLDialect.class.getName());
} else if (driver.toLowerCase().trim().indexOf("postgresql") > -1) { // PostgreSql
p.put("dialect", PostgreSQLDialect.class.getName());
p.put("hibernate.dialect", org.hibernate.dialect.PostgreSQLDialect.class.getName());
} else if (driver.toLowerCase().trim().indexOf("sybase") > -1){ //Sybase
p.put("dialect", SybaseDialect.class.getName());
p.put("hibernate.dialect", SybaseDialect.class.getName());
}
Código E16.28. Algoritmo de definição de dialeto dinamicamente com base na URL do driver JDBC.
Note que, muito embora interessante, este recurso tem margem de erro e alcance limitado aos SGBDs
acima contemplados. Para situações de exceção, deve-se informar “false” na detecção automática (na
anotação ou no arquivo de configuração), informando-se o dialeto diretamente no arquivo
“hibernate.cfg.xml”.
#1. Contrato principal com a camada de persistência. Uma Interface é exigida por muitas
implementações de JPA, que não aceitam o uso de uma classe abstrata como “PlcBaseDAO”
diretamente. Portanto, a Interface “IPlcDAO” foi introduzida ao DP Abstract Factory
implementado por “PlcBaseDAO”.
#5. Classes DAO específicas podem herdar de alguma das implementações concretas de Hibernate
ou JPA, mas não obrigatoriamente. A herança facilita o acesso a sessões de persistência via
“getSession(“minhaFabrica”)” ou “getEntityManager(“minhaUnidadePersistencia”)”,
mas elas podem ser pegas alternativamente via classes de utilitários.
Em Hibernate, por exemplo, pode-se pegar a sessão corrente, de qualquer POJO, via:
#6. O contrato entre a camada de negócio e os serviços de persistência pode ser definido de forma
trivial, através de uma Interface.
#7. Para capturar uma implementação de persistência via Interfaces e DI, deve-se declarar a
Interface na classe Manager de alguma das formas abaixo:
// 3
public class QueFactory extends QueAbstractFactory {
private QueFactory() { }
public static QueFactory getInstance(){
return INSTANCE;
}
@Override
public IQuestionario createQuestionario() throws PlcException {
return (IQuestionario) PlcModeloLocator.getInstance().get(QuestionarioMestreManager.class);
}
}
Código E16.29. Injeção de Dependência com uso de Interfaces - estratégia de Factory.
Note que na segunda variação, a classe de implementação é anotada diretamente. Ela dificulta a
modificação de implementações externamente, mas preserva o baixo acoplamento (já que a
anotação é apenas metadado).
...
public class FuncionarioDAO extends PlcBaseHibernateDAO {
try {
// 1
Session sess = getSession();
// 2
return (Integer) sess.createQuery("select count(*) from FuncionarioEntity f " +
"where f.sexo=:sexo”).setParameter(“sexo”,sexo).uniqueResult();
} catch (Exception e) {
// 3
throw new PlcException("jcompany.erro.generico", new Object[] {
"recuperaTotalPorSexo", e }, e,log);
}
}
…
Código E16.31. Exemplo de implementação de método de DAO com Hibernate.
#1. Captura a sessão corrente de persistência. Todas as sessões devolvida por este método,
durante uma requisição na camada Modelo, retornam a mesma referência (mesma sessão de
persistência).
#2. A execução da cláusula em si deve obedecer à sintaxe HQL para Hibernate ou JPA-QL
para JPA. Pode-se ainda indicar para que mesmo o Hibernate somente aceite sintaxe JPA, em
seu arquivo de configuração “hibernate.cfg.xml”.
Para este caso, basta utilizar o template padrão de tratamento do jCompany, digitando-se
“try” + Control+Space. Uma cláusula de “try – catch” similar à do Código E16.31 será gerada.
Este código padrão realizará um tratamento genérico para exceções externas considerado
“pauta mínima”, incluindo:
...
public class FuncionarioDAO extends PlcBaseJpaDAO {
try {
EntityManager em = getEntityManager();
} catch (Exception e) {
throw new PlcException("jcompany.erro.generico", new Object[] {
"recuperaTotalPorSexo", e }, e,log);
}
}
…
Código E16.32. Exemplo de implementação de método de DAO com JPA.
Neste caso específico, somente a Interface de persistência mudou. Devido à influência que teve Gavin
King, o criador do Hibernate, como líder da especificação que resultou no padrão JPA, o percentual de
similaridade entre as duas implementações chega a 90%.
Do ponto de vista de comunicação entre as camadas, já vimos a Injeção de Dependência em Manager/BO
e AS, via declaração de DAOs ou outros Managers, no construtor das classes:
…
public class CandidatoManager extends PlcBaseBO {
CandidatoDAO candidatoDAO;
AreaProfissionalDAO areaProfissionalDAO;
Importante: Classes DAO não possuem “Template Methods” do jCompany. Não se espera
programações de negócio ou quaisquer outras que requeiram generalizações mesmo parciais, nesta
categoria de classes. Basicamente, elas devem oferecer métodos reutilizáveis de acessos a dados, sem
inclusive envolver muitas trocas de mensagens entre si (orquestrações na camada de Persistência) - pelo
menos, no que tange a persistências para SGBDs relacionais.
Sumário
Neste capítulo, discutimos em maior profundidade diversos aspectos da arquitetura Orientada a Objetos
do jCompany, nas várias camadas do MVC2-P, provendo alta flexibilidade e separação de conceitos que
visam preservar a simplicidade em implementações específicas, da aplicação.
Muitas das complexidades arquiteturais que discutimos têm um propósito bem claro: eliminar código
desnecessário em aplicações de negócio, em todas as camadas, generalizando cenários de Casos de Uso
por completo.
Desenvolvedores acostumados com a programação J2EE deverão ter compreendido o suficiente para
inclusive efetuarem ajustes arquiteturais no jCompany, se necessário for (com uma ajuda complementar
das ferramentas de logging e do depurador do Eclipse, apresentados).
Caso você não tenha compreendido por completo todos os Design Patterns e técnicas OO aqui discutidas,
não se preocupe: a maior parte do conhecimento que foi descrito pode ser abstraído, mesmo em estágios
intermediários de uso do jCompany FS Framework. Ao surgirem demandas que exijam maiores
customizações, retorne a este capítulo.