13
artigo Rafael Ponte ([email protected]): atua com desenvolvimento de software há mais de 4 anos, com foco no desenvolvimento de aplicações Web. Atualmente é sócio e consultor na TriadWorks, entusiasta Java, JSF e por Design de Software. Também é coordenador da lista de discussão JavaServer Faces International Group (JavaSF). Nos tempos vagos, mantém um blog em http://www.rponte.com.br. Tarso Bessa ([email protected]): é um entusiasta Java e atua no desenvolvimento na plataforma há 7 anos. Possui foco em tecnologias Web e gosta de ler bastante sobre novas tecnologias, tendências de mercado e computação distribuída. Atualmente trabalha na Dataprev, é formado pela UNIFOR e possui algumas certificações da Sun, entre elas a SCEA. Tem um blog aqui: http://www.tarsobessa.com. JavaServer Faces introduziu uma arquitetura de componentes de interface na plataforma Enterprise Edition, fazendo com que os desenvolvedores Web, que até então lidavam com frameworks action-based, precisassem mudar a sua forma de construir aplicações e pensassem mais orientada a objetos. Com esse novo modelo, também vieram vícios que os próprios desenvolvedores trouxeram devido a anos de trabalho em frameworks procedurais. Conheça quais são esses maus hábitos e aprenda como eles podem ser evitados. 38 www.mundoj.com.br 38 Descubra quais são os principais erros na construção de aplicações JSF e aprenda como evitá-los. J avaServer Faces (JSF) é a tecnologia padrão da plataforma Enterprise Edition (EE) para construção de aplicações Web. Baseada em uma arquitetura de componentes, JSF promete agilizar o desenvolvimento encapsulando a complexidade de se tra- balhar com a API de Servlets e JSP. Hoje, há uma série de conjunto de componentes que trazem recursos além da especificação, como suporte a temas em CSS, AJAX, Javascript e outras tecnologias que precisam ser combinadas na construção de interfaces para aplicações Web. Antes de JSF, os frameworks MVC consagrados no desenvolvimento, como o Struts, possuíam em comum uma estrutura de processamento de requisições conhecida como action-based e a necessidade de lidar bastante com a especificação de Servlets. O costume de se pensar volta- do a aplicações action-based ainda se manteve em desenvolvedores que Os 10 Maus Hábitos dos Desenvolvedores JSF

Os 10 Maus Hábitos dos Desenvolvedores JSF · começaram a fazer suas primeiras ... ciclo de vida do JSF toma conta de cada instância ... propriedade visible do objeto está com

  • Upload
    lekhue

  • View
    217

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Os 10 Maus Hábitos dos Desenvolvedores JSF · começaram a fazer suas primeiras ... ciclo de vida do JSF toma conta de cada instância ... propriedade visible do objeto está com

a r t i g o

Rafael Ponte

([email protected]): atua com desenvolvimento

de software há mais de 4 anos, com foco no

desenvolvimento de aplicações Web. Atualmente é

sócio e consultor na TriadWorks, entusiasta Java, JSF

e por Design de Software. Também é coordenador

da lista de discussão JavaServer Faces International

Group (JavaSF). Nos tempos vagos, mantém um

blog em http://www.rponte.com.br.

Tarso Bessa

([email protected]): é um entusiasta Java e atua

no desenvolvimento na plataforma há 7 anos. Possui

foco em tecnologias Web e gosta de ler bastante

sobre novas tecnologias, tendências de mercado

e computação distribuída. Atualmente trabalha na

Dataprev, é formado pela UNIFOR e possui algumas

certificações da Sun, entre elas a SCEA. Tem um blog

aqui: http://www.tarsobessa.com.

JavaServer Faces introduziu uma arquitetura de componentes

de interface na plataforma Enterprise Edition, fazendo com

que os desenvolvedores Web, que até então lidavam com

frameworks action-based, precisassem mudar a sua forma de

construir aplicações e pensassem mais orientada a objetos.

Com esse novo modelo, também vieram vícios que os próprios

desenvolvedores trouxeram devido a anos de trabalho em

frameworks procedurais. Conheça quais são esses maus hábitos

e aprenda como eles podem ser evitados.

38 www.mundoj.com.br38

Descubra quais são os principais erros na construção de

aplicações JSF e aprenda como evitá-los.

J avaServer Faces (JSF) é a tecnologia padrão da plataforma Enterprise Edition (EE) para construção de aplicações Web. Baseada em uma arquitetura de componentes, JSF promete

agilizar o desenvolvimento encapsulando a complexidade de se tra-balhar com a API de Servlets e JSP. Hoje, há uma série de conjunto de componentes que trazem recursos além da especificação, como suporte a temas em CSS, AJAX, Javascript e outras tecnologias que precisam ser combinadas na construção de interfaces para aplicações Web.

Antes de JSF, os frameworks MVC consagrados no desenvolvimento, como o Struts, possuíam em comum uma estrutura de processamento de requisições conhecida como action-based e a necessidade de lidar bastante com a especificação de Servlets. O costume de se pensar volta-do a aplicações action-based ainda se manteve em desenvolvedores que

Os 10 Maus Hábitos dos

Desenvolvedores JSF

Page 2: Os 10 Maus Hábitos dos Desenvolvedores JSF · começaram a fazer suas primeiras ... ciclo de vida do JSF toma conta de cada instância ... propriedade visible do objeto está com

3939

— Usar JSTL combinada com componentes JSF

— Usar ‘stateless’ EL em atributos de componentes que disparam eventos

começaram a fazer suas primeiras aplicações em JSF. Isso fez com que eles tivessem bastante dificuldade porque JSF traz uma arquitetura dife-rente, incluindo um mecanismo capaz de disparar eventos semelhantes a Swing, e também incompatibilidades com JSP e JSTL. Esse modo de programação criou maus hábitos na utilização dessa tecnologia nova.

O objetivo deste artigo é discutir os dez erros mais comuns que de-senvolvedores iniciantes na tecnologia cometem, principalmente por trazerem uma bagagem de muitos anos desenvolvendo em outros tipos de frameworks MVC. Os tópicos deste artigo estão organizados de uma forma que permitem a leitura individual, pois o mínimo de conheci-mento necessário para a compreensão é fornecido em cada tópico, mas eventualmente é necessário conhecer o ciclo de vida da tecnologia, que pode ser conferido no 1º mau hábito. Todas as discussões neste artigo são frutos da experiência profissional dos autores durante o desenvolvi-mento de vários sistemas com o uso da tecnologia JavaServer Faces (JSF), de suas discussões em fóruns, listas de discussão e também provenientes de consultoria para algumas empresas.

JSTL é a biblioteca de tags padrão da especificação JSP que fornece uma série de tarefas de controle de fluxo, formatação de dados, manipulação de XML e outras operações. Esta biblioteca é indispensável no desenvol-vimento Web em sua forma mais tradicional. A versão 2.0 de JSP ainda trouxe um novo recurso chamado Expression Language, que combinado com JSTL, tornou a manipulação dinâmica de conteúdo muito mais simples. O trecho de código a seguir exemplifica como é essa geração dinâmica de conteúdo usando JSTL:

A primeira versão de JSF mudou completamente o modo de desen-volver porque trouxe para a plataforma uma arquitetura baseada em componentes, inexistente até então nas especificações padrões, e uma Expression Language totalmente incompatível com JSP. Um componen-te JSF tem três partes: uma classe de tag, uma classe de componente e outra específica para renderização do conteúdo do componente. Muitos desenvolvedores ainda confundem as tags dos componentes JSF com os próprios componentes JSF, mas, na verdade, o único papel das tags é instanciar os componentes e inicializar os atributos e, a partir daí, o ciclo de vida do JSF toma conta de cada instância dos componentes tor-nando as tags coadjuvantes. Essas diferenças na arquitetura provocaram incompatibilidades entre as tags tradicionais de JSP (incluindo JSTL) e os componentes JSF, pois as tags tradicionais têm seu conteúdo resolvido externamente ao ciclo do JSF, o que dificulta bastante a integração. Mes-mo com a EL Unificada, criada em JSP 2.1 e JSF 1.2, as incompatibilidades ainda existem.

Uma página com componentes JSF se transforma em uma árvore de objetos que é persistente entre requisições e controlada pela própria tecnologia. Quando JSTL é usada em conjunto com componentes JSF, há a possibilidade das tags JSTL estarem alterando dinamicamente essa árvore de componentes, destruindo ou criando instâncias novas de com-

ponentes. Esse comportamento pode provocar erros no código de um Managed Bean que tenha uma referência a uma instância de um com-ponente através de binding, pois haverá momentos em que a referência estará nula, o que não aconteceria usando apenas JSF. O efeito colateral de se remover ou criar instâncias é fazer com que os componentes per-cam seus valores locais, ou seja, todos os atributos de um componente modificados por um Managed Bean terão seus valores descartados. A Listagem 1 exibe um exemplo da tag <c:if> de JSTL sendo usada para controlar a visibilidade de um componente JSF.

Solução

Os componentes visuais em JSF possuem um atributo chamado rendered, que podem receber uma EL que avalia para true ou false. Se avaliada para true, o componente é exibido (ou renderizado) e se avaliada para false, o componente não é renderizado. A diferença com relação à JSTL é que o componente vai sempre existir na árvore de componentes e, portanto, terá seu estado persistido entre as requisições. Dessa forma, é seguro um Managed Bean obter uma referência para um componente e usá-lo da for-ma como bem entender. A Listagem 2 exemplifica como os componentes visuais podem ter sua visibilidade através do atributo rendered.

Os cuidados com a JSTL não param por aí. Por exemplo, se a tag c:forEach for combinada com JSF, ela vai criar sempre uma nova instância de um componente para cada item que está iterando. Isso é muito diferente do comportamento de UIData (classe base da h:dataTable), por exemplo, que possui uma única instância de cada componente filho e a reusa para cada item de sua coleção, sendo mais econômica em recursos. A tabela 1 mostra componentes JSF que podem ser usados para substituir algumas tags em JSTL.

Um dos requisitos impostos pela arquitetura de componentes da tec-nologia JSF é que a aplicação precisa ser stateful, ou pelo menos parte dela. Os dados que alguns componentes manipulam precisam estar disponíveis durante várias requisições para que o comportamento do componente seja estável com relação a eventos, ou até mesmo para que certos componentes consigam editar valores em objetos gerenciados.

<c:if test=”${user.admin}”> Bem vindo, admin!</c:if>

<c:if test=”#{bean.admin}”> <!-- esse componente pode sumir da árvore e provocar erros --> <h:inputText value=”#{bean.value}/></c:if>

<!-- Um panelGroup pode ser usado para englobar mais de um componente --><h:panelGroup rendered=”#{bean.admin}”> <h:inputText value=#{bean.value}/> <h:commandButton value=”Submit”/></h:panelGroup>

Listagem 1. Mau hábito — usando JSTL para controlar visibilidade de componentes JSF.

Listagem 2. Uso do atributo rendered para controlar a visibilidade de componente(s).

Page 3: Os 10 Maus Hábitos dos Desenvolvedores JSF · começaram a fazer suas primeiras ... ciclo de vida do JSF toma conta de cada instância ... propriedade visible do objeto está com

Cenário Em JSTL Em JSF Considerações

Controlando a

visibilidade de um

trecho da página

c:if Atributo rendered

dos componentes

Não modifica a árvore

de componentes.

Controlando a

visibilidade em

grupo

c:choose Componente

h:panelGroup

Não é igual a

c:choose, mas

agrupa logicamente

componentes e possui

o atributo rendered.

Iterando itens em

formato tabular

c:forEach Componente

h:dataTable

Possui melhor

gerenciamento de

componentes filhos,

além de gerenciar

melhor seus ids..

Iterando itens com

estrutura livre

c:forEach - t:dataList, do

MyFaces Tomahawk

- tr:iterator, do

MyFaces Trinidad

As mesmas vantagens

que a h:dataTable,

mas com layout livre.

É comum associar uma propriedade de um Managed Bean a um atributo de um componente através de Expression Language. A EL que represen-ta essa ligação é conhecida como Value Expression (antigamente, Value Binding). A Listagem 3 mostra um exemplo de uma EL associada ao atributo rendered de um componente.

Quando se associa uma expressão a um componente, é preciso ter em

mente em quais momentos o componente vai necessitar daquele valor,

pois, dependendo do caso, pode ser possível que um componente pre-

cise daquele valor até mesmo entre requisições, afinal os componentes

são persistentes (stateful). Um desses casos se refere a ELs associadas ao

atributo rendered.

O que se encontra em documentações é que o rendered controla se

um componente terá seu conteúdo gerado na resposta, ou seja, visível

ao usuário, mas não é apenas isso. Esse atributo também controla se o

componente deverá se processar no início de uma nova requisição, pois,

se ele não é renderizável no início do ciclo, supõe-se que o usuário não

conseguiu interagir com o componente, logo, o componente nem exa-

mina o que vem nessa nova requisição se o atributo rendered for false.

As expressões associadas ao rendered são avaliadas em dois momentos,

durante a fase Apply Request Values, que é a segunda fase do ciclo e na

fase Render Response, que é a fase que gera a resposta dos componen-

tes. Se a expressão avaliar para false durante a Apply Request Values, o

componente não vai receber os dados da requisição, consequentemente,

não vai se processar, nem empilhar eventos. É preciso ter muito cuidado

quando se usa expressões no atributo rendered que dependem de Ma-

Eventos da 1ª Requisição Eventos da 2ª Requisição

1. Ao iniciar uma requisição,

um componente é criado e

tem seu atributo rendered

associado à expressão #{bean.

visible}. Ex.: <h:commandLink

rendered=”#{bean.visible}”/>

2. O objeto bean é instanciado

e atribuído ao escopo request. A

propriedade visible do objeto está

com o valor padrão para boolean,

que é false.

3. Em outro momento da aplicação,

o desenvolvedor atribui true a

propriedade visible de bean.

4. Na Render Response, a expressão

#{bean.visible} é avaliada para true

e o componente é renderizado.

5. A requisição acaba e o objeto

bean é destruído.

1. A requisição 2 começa quando o usu-

ário aciona o componente renderizado

anteriormente, o commandLink.

2. Na fase Apply Request Values, os

componentes irão obter seus valores

da requisição, exceto aqueles cujos

atributos rendered possuem valor false.

3. O commandLink do exemplo

avaliará a expressão #{bean.visible} e

o Container vai procurar um objeto de

nome bean em um dos escopos.

4. O objeto bean não existe porque ele

foi destruído na última requisição.

5. O Container vai criar o objeto com os

valores padrões, logo, a propriedade

visible ganha o valor false.

6. O commandLink avalia a expressão

#{bean.visible} para false e não se

processa, ou seja, não empilha o evento

e nenhum método será chamado.

7. O fluxo vai até o final e a página é

reexibida sem com que o evento seja

processado.

Tabela 2. Exemplo detalhando o comportamento de componentes quando associados à Ma-

naged Beans com escopo curto demais.

O termo stateless EL não existe na literatura, mas a palavra stateless é a me-

lhor para descrever expressões que avaliam de forma inconsistente entre

requisições, ou seja, não guardam estado entre duas ou mais requisições

consecutivas na qual um componente depende.

Ter um modelo stateless influencia também no comportamento de even-

tos de outros componentes. O componente UIInput, por exemplo, dispara

eventos para notificar mudança de valores sempre que a sua propriedade

value sofrer alterações. Se houver uma EL associada à propriedade value, o

componente vai avaliar a EL e comparar o valor antigo com o valor novo. Se

forem diferentes, o evento é disparado. Agora, o que acontece se a EL asso-

ciada ao value de um UIInput depender de objetos em escopo de requisi-

ção? O evento de mudança acaba sendo disparado sempre, mesmo que o

valor não mude aos olhos dos usuários. Com certeza esse comportamento

não é desejado, mas acontece porque objetos no escopo de requisição têm

o ciclo de vida muito curto e são descartados ao final das requisições junto

com seus dados internos. Na requisição seguinte, eles são recriados com os

valores padrões e o UIInput acaba avaliando a expressão que retorna um

valor diferente do anterior, provocando o evento.

naged Beans em escopo de requisição, especialmente se o componente estiver presente em múltipas requisições. A tabela 2 mostra em detalhes um exemplo para esse cenário. Na maioria dos casos, os desenvolvedores associam uma expressão que depende de objetos no escopo de requi-sição fazendo com que a expressão avalie para true na fase de rende-rização da primeira requisição, mas na requisição seguinte, a expressão acaba avaliando para false porque o Managed Bean não existe mais e o componente não se processa. CommandButtons e CommandLinks são vítimas frequentes dessa forma de programação

40 www.mundoj.com.br

<!-- Visível apenas na edição --><h:commandButton rendered=”#{bean.editing}” value=”Update” action=”...”/>

<!-- Visível apenas na inserção --><h:commandButton rendered=”#{bean.inserting}” value=”Save” action=”...”/>

Listagem 3. Value Expression associando a propriedade de um Managed Bean com o atributo rendered de um componente.

Page 4: Os 10 Maus Hábitos dos Desenvolvedores JSF · começaram a fazer suas primeiras ... ciclo de vida do JSF toma conta de cada instância ... propriedade visible do objeto está com

Solução

Existem algumas estratégias para se resolver esse tipo de problema. Uma delas é ampliar o escopo dos objetos dos quais as ELs depen-dem e garantir que as ELs sejam avaliadas de um modo consistente entre requisições.

Nem sempre ampliar o escopo de um objeto (ou um Managed Bean) significa ter que colocá-lo na sessão. Os objetos que residem na ses-são persistem até a sessão ser invalidada ou ter seu tempo expirado e, às vezes, esse período pode ser muito longo para que todo tipo de objeto seja armazenado lá. Para se livrar desses comportamentos descritos anteriormente, os Managed Beans acabam sendo declara-dos em escopo de sessão prejudicando a escalabilidade (pelo fato da sessão estar sendo usada indiscriminadamente) e a usabilidade do próprio usuário, que não vai conseguir abrir duas abas do navegador e executar mais de uma tarefa sem que uma influencie na outra.

Por limitações da própria especificação de Servlets e JSP, que só disponibiliza três escopos, as soluções mais indicadas giram em torno de contextos de conversação, que duram apenas por um determinado tempo, como é o caso do JBoss Seam e do MyFaces Or-chestra. Além dessas opções, há o pageFlowScope, um escopo que persiste ao longo do fluxo de navegação de páginas, do MyFaces Trinidad e outros dois similares, o t:saveSate do MyFaces Tomahawk e o a4j:keepAlive do JBoss RichFaces, que guardam os objetos com a árvore de componentes.

— Usar <redirect/> nas regras de

navegação para forçar a mudança de URL

Uma das mudanças trazidas por JSF é a ausência de processamento

baseado em URLs, como é bem comum em frameworks action-

based. Toda ação que acontece em uma aplicação JSF é fruto de um

evento disparado por um componente, então não há como mapear

uma URL para um método de uma classe ou um Managed Bean.

Em JSF, o desenvolvedor não informa para onde as requisições

estão indo, como nos frameworks action-based. Ao invés disso, o

Container se preocupa em gerar uma URL que apenas passe pelo

FacesServlet, que é o componente principal da tecnologia. Somado

a isso, o padrão das navegações é server-side, ou seja, o navegador

não percebe quando ocorre a navegação entre páginas. O resultado

é uma URL na barra do navegador sempre desatualizada com a

página atual.

Esse comportamento do UIInput não é tão impactante para a aplicação porque o valor do componente acaba sendo atribuído à EL e a aplicação pode seguir com o seu fluxo. Já a UIData (classe base para Datatables) atua de maneira diferente quando uma EL é associada a sua propriedade value. Normalmente, associa-se uma lista de objetos ou uma instância de javax.faces.model.DataModel contendo os itens a serem exibidos. Se essa EL depender de algo em escopo curto demais e na requisição seguinte o valor for avaliado para nulo (ou lista vazia), nenhum evento de componentes internos à UIData será processado, muito menos nenhum dado alterado.

41

Muitas soluções de segurança para aplicações JEE são baseadas em filtros HTTP, seguindo o padrão Intercepting Filter. Um filtro capta a requisição antes de chegar ao FacesServlet. O que acontece é que filtros configurados para capturarem requisições não conseguem capturar a URL da página atual corretamente, pelo fato de JSF não ser voltado a URLs.

Para solucionar este problema e forçar sempre o acesso à página atual através do navegador, alguns desenvolvedores se utilizam da tag redirect nas regras de navegação do arquivo faces-config.xml. Essa tag indica ao Container que aquela regra de navegação deve gerar uma nova requisição. A Listagem 4 mostra uma tag redirect sendo usada para provocar um redirecionamento entre páginas que podem ser de um mesmo caso de uso.

Muitos desenvolvedores pensam que a única finalidade da tag redirect é basicamente mudar a URL na barra de endereço do navegador durante a navegação entre páginas. Ela realmente muda e o filtro de segurança passa a capturar a URL correta, mas aí é que começam os problemas, pois a aplicação começa a comportar-se de maneira estranha. Como um redirecionamento implica em uma segunda requisição, os Managed Be-ans em escopo de requisição têm seus valores perdidos e as mensagens de sucesso (ou erro) adicionadas ao FacesContext não são mais exibidas ao usuário como esperado. Prever quais os possíveis problemas do uso indevido da tag redirect não é uma tarefa tão simples assim.

Solução

Pode-se dizer que a solução mais simples seria apenas a remoção da tag redirect da regra de navegação ou a utilização de um framework como JBoss Seam. Para a questão do filtro de segurança, basta con-figurar o filtro para capturar forwards (se a aplicação estiver usando JSF e JSP). Mesmo assim, o problema é bem mais grave do que muitos imaginam.

A solução mais apropriada, na verdade, é o desenvolvedor entender os fundamentos básicos sobre desenvolvimento Web. Mais espe-cificamente, para este mau hábito, o desenvolvedor deve saber a diferença entre um forward e um redirect, e quando usar cada um deles durante a navegação entre páginas na aplicação.

Um Servlet (controller) pode realizar tanto um forward ou um redirect no fim do processamento de uma requisição. Entender a diferença entre eles é realmente importante, principalmente quan-do se considera a possibilidade do usuário fazer reload das páginas pelo navegador. A tabela 3 sintetiza as características impostas por forwards e redirects.

<navigation-rule> <from-view-id>/pages/pesquisa.jsp</from-view-id> <navigation-case> <from-outcome>cadastrar</from-outcome> <to-view-id>/pages/cadastro.jsp</to-view-id> <redirect /> </navigation-case></navigation-rule>

Listagem 4. Mau hábito — Usando redirect para forçar mudança na URL.

Page 5: Os 10 Maus Hábitos dos Desenvolvedores JSF · começaram a fazer suas primeiras ... ciclo de vida do JSF toma conta de cada instância ... propriedade visible do objeto está com

42 www.mundoj.com.br

Solução

Forward Redirect

está ocorrendo durante o processamento no servidor, ou seja, não sabe por quais Servlets ou páginas a requi-sição está passando.

da requisição, a URL da bar-ra de endereços do navega-dor não muda.

-te irá executar a requisição original.

ao receber uma requisição a aplicação web “pede” ao na-vegador para acessar uma segunda URL. Por isso, a URL muda.

a requisição original, mas sim a nova URL (2ª requisição).

-to que um forward, pois são necessárias duas requisições, e não uma.

-po de requisição original são perdidos durante a segun-da requisição, assim como as mensagens.

Em geral, um forward deve ser utilizado em operações que podem ser seguramente repetidas pelo navegador, por outro lado, se uma operação modificar registros no banco de dados, um redirect deve ser utilizado, aplicando a estratégia Redirect After Post. Desta maneira, evita-se que um reload na página provoque a possibilidade de inadvertidamente duplicar registros no banco de dados (um problema conhecido como double submit). JSF puro não possui um mecanismo completo para Re-direct After Post. Para isso, precisa-se usar o JBoss Seam ou implementar a própria solução para que as mensagens adicionadas ao FacesContext sejam preservadas.

Caso não se deseje utilizar o padrão Redirect After Post, tem-se a opção de controlar o problema do double submit através de tokens. Assim, um reload pode ser impedido porque o token da requisição seguinte já foi processado. O JBoss Seam, mais uma vez, traz uma solução para isso.

— Usar Javascript para alterar o estado de componentes

Em aplicações Web construídas com um framework mais simples, os desenvolvedores costumam enriquecer o desenvolvimento agregando outros recursos e linguagens, como Javascript, que muitas vezes são usados para alterar o conteúdo de uma página no lado cliente (no na-vegador). Este tipo de prática torna as aplicações muito mais interativas com os usuários, mas também podem trazer maus hábitos se não for usada corretamente com JSF.

JSF é uma tecnologia em que todo o seu processamento ocorre no lado servidor e, além disso, tem uma estrutura baseada em componentes que realizam o processamento das requisições. Os componentes são man-tidos em uma estrutura de árvore para representar uma página e esta árvore é persistente entre requisições. Nada além dos componentes que estão na árvore será processado em JSF.

Alguns desenvolvedores, por terem vindo de frameworks action-based,

Por mais que o input text tenha seu valor alterado e o read-only remo-vido na página gerada pelo JSF, o valor desse componente não será avaliado no ciclo de vida, pois no lado servidor o componente continua possuindo o estado read-only e, consequentemente, a tecnologia res-peita isto. Desta forma, durante o processamento do componente, o framework irá ignorar qualquer valor submetido pelo usuário. O mesmo comportamento se aplica também a componentes de seleção múltipla (como o h:selectOneMenu) em que o desenvolvedor insere ou remove itens dinamicamente via Javascript, e espera que ao submeter o formu-lário, a tecnologia JSF entenda e permita que estes novos itens sejam processados normalmente.

Não é que não seja possível usar Javascript com JSF, pelo contrário, muitos conjuntos de componentes usam bastante Javascript para ter recursos como AJAX, como é o caso do RichFaces e o MyFaces Trinidad. Este último, por exemplo, possui uma API de validação client-side toda construída em Javascript. A questão é não usar Javascript para alterar o estado dos componentes.

Para mudar este mau hábito, é necessário que o desenvolvedor altere o estado do componente (ou Managed Bean) no lado servidor via AJAX e ren-derize novamente o componente com o novo estado para o usuário. Assim, qualquer mudança necessária na GUI será informada ao JSF, processada e, por fim, refletida para o usuário após a atualização parcial da página.

Infelizmente, algumas aplicações, por motivos particulares, não podem

como o Struts, têm o hábito de alterar a estrutura da página via Javas-cript por vários motivos, seja por questões de usabilidade ou facilidade, e no final esperam ter esta alteração refletida no lado servidor. Como os componentes JSF se processam apenas no servidor, tentar adicionar um elemento HTML ou até mesmo tentar modificar o valor de um compo-nente que é read-only (somente leitura) ou disabled, é inútil. A insistên-cia neste (mau) hábito acaba frustrando os desenvolvedores porque suas alterações no lado cliente não refletem como o esperado na aplicação.

Enquanto uns podem reclamar que esse modelo proposto pelo JSF não é flexível, vale ressaltar que esse modelo promove uma maior segurança, pois um usuário mal intencionado não vai conseguir, através da alteração do DOM (Document Object Model) de uma página, modificar valores no servidor. Um bom exemplo disso pode ser mostrado na figura 1, em que se tenta usar o Firebug, um plugin para o navegador Firefox, para modi-ficar informações da página.

Page 6: Os 10 Maus Hábitos dos Desenvolvedores JSF · começaram a fazer suas primeiras ... ciclo de vida do JSF toma conta de cada instância ... propriedade visible do objeto está com

43

Solução

— Utilização de parâmetros de requisição

O envio de informações de um navegador para um servidor se dá por meio de parâmetros, devido a natureza do protocolo HTTP. Então, baseado nisso, o desenvolvimento na web, usando a API de Servlets, tem sido por muito tempo uma troca de valores que eram usados para identificar o que o usuário estava fazendo e assim continuar um processo. Claro que algumas informações poderiam ficar no escopo de sessão, mas para não prejudicar a escalabilidade, as informações que ficam na sessão normalmente são aquelas que são restritas ao usuário e não ao processo que ele está fazendo (levando em consideração uma época sem gerenciamento de sessão, pré JBoss Seam).

Se fosse necessário disparar um processo que dependesse da identificação de um dado que estivesse armazenado no banco de dados, era muito co-mum usar a chave primária do objeto para identificar a escolha do usuário. Por exemplo, para remover um item em um cadastro, um link poderia dis-parar uma ação e enviar como parâmetro o valor que identificasse o objeto a ser removido, como mostrado na Listagem 5. No servidor, o parâmetro seria usado para fazer uma consulta, obter o registro e removê-lo.

Quando se traz essa cultura para aplicações JSF, os componentes acabam sendo subutilizados e se tornam meros substitutos para tags HTML. Isso é muito ruim porque o desenvolvedor acaba programando o que o compo-nente poderia estar fazendo para ele. Na Listagem 6, o exemplo anterior é convertido para JSF, mas ao invés de se usar o CommandLink para realizar uma ação, o componente é usado apenas para passar um parâmetro.

O código que interpreta o parâmetro é mostrado na Listagem 7, logo a seguir. O problema com o código é a manipulação direta do parâmetro extraído da requisição. Para que o objeto seja identificado, ainda é ne-cessário converter para um tipo do domínio da aplicação e a partir daí, usar o valor.

Os componentes JSF conseguem otimizar esse tipo de tarefa para que o desenvolvedor não precise programar baseado em parâmetros. Por exemplo, um componente de seleção, como o <h:selectOneMenu>, con-segue saber qual foi o item selecionado (ver o quadro Métodos equals e hashCode) e atribuir o item a uma propriedade de um Managed Bean. Dessa forma, o desenvolvedor lida direto com o objeto e deixa o trabalho de manipular parâmetros para o próprio componente. A Listagem 8 mostra como o mesmo problema pode ser simplificado usando de uma melhor forma os componentes.

adotar AJAX para um desenvolvimento mais rico. Logo, a equipe de desenvolvedores acaba sendo obrigada a atualizar a página completa-mente após alterar o estado de algum componente.

O código também fica mais legível, pois agora o CommandLink vai atribuir o objeto selecionado à propriedade de um Managed Bean. Para desenvolver bem em JSF, é preciso pensar mais orientado a objetos e deixar com que os componentes realizem as tarefas repetitivas, para que haja ganho de produtividade. A Listagem 9 mostra como fica o código do Managed Bean.

<table ...> <tr> <td>John Doe</td> <td><a href=”/context/process/remove?id=5” >Remove...</a></td> </tr></table>

<h:dataTable value=”#{users}” var=”user”> <h:column ...> <h:commandLink value=”X” action=”#{bean.remove}” > <!-- Passagem de parâmetro em links --> <f:param name=”id” value=”#{user.id}”/> </h:commandLink> </h:column> </h:dataTable>

public void remove(){ //Converte o valor manualmente Integer id = new Integer( facesContext.getExternalContext(). getRequestParametersMap(). get(“id”) ); User user = search(id); if(user != null){ //manipula o objeto. }}

Listagem 5. Link passando um parâmetro que identifica um objeto a ser usado.

Listagem 6. Mau hábito — Uso de componentes para passagem de parâmetros.

Listagem 7. Exemplo de como obter parâmetros da requisição HTTP usando JSF.

<h:dataTable value=”#{users}” var=”user”>

<h:column ...>

<h:commandLink value=”X” action=”#{bean.remove}” >

<!-- Realiza a atribuição do objeto selecionado -->

<f:setPropertyActionListener value=”#{user}” target=”#{bean.user}”/>

</h:commandLink>

</h:column>

</h:dataTable>

public class Bean { public void setUser(User user){ this.user = user; } public void remove(){ //não precisa pesquisar mais, pois a atribuição é feita pelos componentes. if(user != null){ // manipula o objeto } }}

Listagem 8. Exemplo de como obter um objeto selecionado em uma Datatable.

Listagem 9. Código de um managed bean que tem objetos atribuídos por componentes.

Page 7: Os 10 Maus Hábitos dos Desenvolvedores JSF · começaram a fazer suas primeiras ... ciclo de vida do JSF toma conta de cada instância ... propriedade visible do objeto está com

44 www.mundoj.com.br

Como JSF trabalha muito com objetos e coleções, especial-mente listas, é essencial que os métodos equals e hashCode sejam implementados nos objetos de modelo da aplicação. Por exemplo, os componentes de seleção sempre verificam se o item selecionado pertence à coleção de dados que foi usada para renderizar as opções. Se os métodos equals e hashCode não forem implementados, o componente vai disparar um erro informando que aquele valor é inválido, pois mesmo que exista um elemento na lista de opções com os mesmos dados, sem equals e hashCode fica impos-sível determinar se os dois objetos são idênticos.

— Usar o valor de submittedValue como se fosse o valor real de um componente

É comum uma página de uma aplicação ter mais de um botão que dis-pare eventos. Alguns desses eventos irão obter os dados do formulário e processá-los da maneira mais adequada, porém, em algumas situações, certos eventos não precisarão manipular dados, pois eles servirão apenas para cancelar uma operação e navegar para outra página. Em JSF, alguns componentes possuem um atributo chamado immediate capaz de alte-rar o comportamento dos componentes para que eles se processem em fases diferentes, incluindo a chamada a seus eventos.

Os componentes não-imediatos processam seus eventos na fase Invoke Application, ou seja, após a validação, conversão e atualização de valo-res em Managed Beans. Dessa forma, é seguro obter um valor de uma propriedade gerenciada por um componente e armazená-la em uma base de dados, por exemplo. Já os componentes imediatos processam seus eventos na fase Apply Request Values, ou seja, logo após recebe-rem os seus dados da requisição e isso é antes da validação, conversão e atribuição de valores em Managed Beans. Um componente imediato, por exemplo, seria o responsável por navegar para outra página, fazendo com que o usuário saia de uma página que tenha um formulário de dados a ser preenchido. Esse exemplo é o de um botão Voltar ou Cancelar em uma página de cadastros que tenha campos obrigatórios. A Listagem 10 mostra um exemplo de um formulário com dois botões, um imediato e outro não-imediato.

Existe um desentendimento bem comum por parte de alguns desenvol-vedores que pensam que os eventos de componentes imediatos pulam a etapa de validação (Process Validations). Isso não é completamente cor-reto. O correto é afirmar que um componente imediato se processa antes da etapa de validação. Se o componente for um UICommand (botões e links), o evento interrompe o ciclo evitando a etapa de validação, mas se o componente for EditableValueHolder (inputs e selects), o componente se processa por inteiro, incluindo a sua própria validação, e o ciclo de vida ainda continua disparando mais adiante a validação dos componentes não-imediatos. Se houverem dois ou mais inputs imediatos junto com um botão imediato em um formulário, os inputs serão validados antes do evento do botão ser chamado. Nesse caso, mesmo todos sendo ime-diatos, os inputs se validarão antes da chamada ao do evento do botão.

Existem certos cenários em que é preciso obter o valor de um componen-te dentro de um evento disparado por outro componente imediato. Um exemplo disso é um botão que, quando acionado, obtenha o valor de um input, calcule um valor qualquer e exiba no formulário. Se o formulário tem 10 campos obrigatórios, seria indesejável obrigar o usuário a digitar cada um dos campos só para poder acionar o botão de cálculo. Portanto, esse botão tem chances de ser imediato. Assim, a ação acontece antes da validação dos demais campos e o usuário não precisa digitar campos que não tenham nada a ver com aquela informação.

Como obter o valor de um componente dentro de um evento imediato?

Se o evento é imediato, o valor não estará na propriedade de uma Ma-naged Bean, portanto só há uma alternativa: realizar um binding de um componente e obter o valor diretamente da instância do componente. Aqui começa a armadilha!

Durante a Apply Request Values, que é a fase onde os eventos imediatos são chamados, é muito cedo para se obter o valor de um componente. O principal objetivo dessa fase é para que cada componente guarde o valor que lhe foi informado pela requisição. Esse valor é uma espécie de cache e é armazenado na propriedade submittedValue do próprio componen-te. O ciclo de vida do JSF prevê o seguinte: se as validações falharem, os dados em Managed Beans não serão atualizados e os valores informados pelo usuário serão reexibidos no formulário, junto com as mensagens de erro produzidas por cada validador ou conversor associado a cada componente. O que é reexibido é esse cache, ou seja, o submittedValue.

Como os dados enviados em formulários HTML são sempre String, os da-dos armazenados também são sempre em String, independentemente se o componente está ligado a uma propriedade de um Managed Bean de outro tipo (ex.: java.lang.Integer, java.util.Date etc.). O problema é que esse é o único valor que está disponível em um componente durante um evento imediato, fazendo com que o desenvolvedor o use para saber qual foi o dado digitado ou selecionado pelo usuário. As Listagens 11 e 12 mostram, nessa ordem, um exemplo de uso com componentes imediatos e um mau exemplo de como obter o valor de um componente usando o submittedValue.

O valor que é atribuído ao Managed Bean, na fase Update Model Values, não é o valor do submittedValue (exceto se a propriedade do Bean for String, claro), e sim um valor validado e convertido de acordo com o tipo da propriedade do Bean ou de acordo com os validadores e conversores do componente. É importante ressaltar que o valor armazenado em

<h:form>

...

<h:inputText required=”true” value=”#{bean.name}”/>

<!-- Só dispara o evento se o campo ‘nome’ acima for preenchido -->

<h:commandButton value=”Salvar a Informação” action=”...”/>

<!-- Ignora o preenchimento do nome e navega para outra página -->

<h:commandButton value=”Ir para o Index” immediate=”true” action=”...” />

</h:form>

Listagem 10. Exemplo de botões imediatos e não-imediatos.

Page 8: Os 10 Maus Hábitos dos Desenvolvedores JSF · começaram a fazer suas primeiras ... ciclo de vida do JSF toma conta de cada instância ... propriedade visible do objeto está com

45

submittedValue não reflete, na maioria das vezes, o valor da propriedade do Bean, então essa propriedade não deve ser usada para obter o valor de um componente.

A tentação cometida por desenvolvedores é obter esse valor em String e realizar a conversão e validação manual dentro de um evento imedia-to em um Managed Bean. O problema com esta abordagem é que o componente perde a utilidade de automatizar tarefas repetitivas, como validação e conversão, além do código ter grandes chances de ser alte-rado para manter a sincronia sempre que um novo comportamento for adicionado ao componente. Por exemplo, se o componente possui dois validadores associados, um que verifica qual a data mínina que pode ser fornecida e outro que verifica o intervalo máximo de um período, o de-senvolvedor precisa programar esse mesmo comportamento no método de um Managed Bean para manter a consistência dos dados.

Solução

Uma solução parcial para este tipo de problema é marcar todos os outros componentes que serão utilizados dentro de um evento imediato como imediatos. Dessa forma, o componente se processa na mesma fase e já consegue produzir o valor que será atribuído ao Managed Bean em fases futuras. A Listagem 13 mostra um exemplo de como fica o código se todos os componentes forem imediatos.

O código da Listagem 13 está mais adequado porque o componente está realmente sendo utilizado. Dentro do Managed Bean não há mais métodos para converter e validar. Nesse cenário, a chamada ao método isValid se faz necessária porque os eventos imediatos acontecem sem-pre na mesma fase, então uma falha de validação não impede outros eventos da mesma fase de serem chamados. Falha de validação só impede a fase seguinte de executar. Se o botão é imediato e precisa obter o valor de um input também imediato, ele precisa checar se o valor é válido.

Essa solução é parcial porque acaba produzindo outro problema. Se existir um terceiro componente imediato, como um botão Voltar, ele acaba fazendo com que todos os inputs imediatos sejam também processados. Isso faz com que um botão imediato, que apenas precise navegar para outra página, dispare a validação de um input imediato e que pode produzir uma mensagem de erro de validação. Em JSF, não há uma maneira de desligar o mecanismo de validação. A solução definitiva é usar Subforms.

Subforms separam o formulário principal em regiões independentes. Todo evento que acontece dentro de uma área dessas permanece na-quele escopo até o fim da requisição e as demais regiões têm seus da-dos preservados. Com essa estratégia, é possível deixar em um subform aqueles componentes que precisam executar um fluxo à parte do fluxo principal de um formulário, reduzindo ou anulando a necessidade de componentes imediatos. Componentes de subforms estão presentes nos seguintes kits de componentes, Tomahawk (t:subform), Trinidad (tr:subform) e RichFaces (a4j:region). As Listagens 14 e 15 mostram, respectivamente, como ficariam a página e o código do Managed Bean refatorados com Subforms.

<h:form>

...

<h:inputText required=”true” value=”#{bean.name}” />

...

<h:inputText binding=”#{bean.iptDate}” required=”true” value=”#{bean.date}”>

<f:convertDateTime pattern=”dd/MM/yyyy”/>

</h:inputText>

<!-- Botão precisa ser imediato -->

<h:commandButton immediate=”true” value=”Calcular Valor” action=”...”/>

<h:inputText required=”true” … />

...

</h:form>

public class Bean {

//Binding para um componente que edita datas

private UIInput iptDate;

//Evento imediato. Obtém valores através de bindings de outros componentes

public void calcValue( ActionEvent evt ){

String dateStr = (String) iptDate.getSubmittedValue();

/**

* O problema está na manipulação desse dado. Se o valor usado é String e

* a conversão/validação é manual, para que serve o componente?

*/

Date dateValue = convert( dateStr );

if ( dateValue != null && dateValue.after( otherDate ) ) {

//realiza a ação

}

}

}

Listagem 11. Formulário com um botão imediato, que precisa executar uma ação antecipadamente.

Listagem 12. Mau hábito — Obtendo o valor de um componente do formulário através de submittedValue.

public class Bean { //Binding para um componente que edita datas. //Marcado como immediate=true private UIInput iptDate; //Evento imediato. public void calcValue( ActionEvent evt ){ /** * O código foi refatorado porque o componente iptDate é imediato e já * possui o valor em sua propriedade value. A conversão e a validação * passa a ser feita pelo próprio componente. */ if ( iptDate.isValid() ) { Date dateValue = (Date) iptDate.getValue(); if ( dateValue.after( otherDate ) ) { //realiza a ação } } }}

Listagem 13. Solução não definitiva — marcando todos os componentes envolvidos como imediatos.

Page 9: Os 10 Maus Hábitos dos Desenvolvedores JSF · começaram a fazer suas primeiras ... ciclo de vida do JSF toma conta de cada instância ... propriedade visible do objeto está com

46 www.mundoj.com.br

Como subforms permitem que as áreas sejam processadas indepen-dentes, não se faz necessário marcar os componentes como imediatos. Resultando em um código no Managed Bean mais simples e deixando o trabalho de conversão e validação para os componentes.

— Implementar o próprio mecanismo de segurança

É fato que uma aplicação corporativa precisa usar uma solução de segu-rança e as aplicações construídas em JSF não estão isentas dessa possi-bilidade. Segurança em aplicações na plataforma JEE geralmente é feita com filtros, JAAS ou uma combinação de ambos. O problema é quando os desenvolvedores encontram uma aplicação que acham simples de-mais e decidem criar um mecanismo novo baseado em PhaseListeners, com a desculpa de que vai ser apenas para aquela aplicação.

O problema com esta abordagem é que a solução fica completamente atrelada à tecnologia JSF, ou seja, outras aplicações feitas em outras tec-nologias Web, na mesma empresa, que necessitam de um mecanismo de segurança não vão aproveitar a lógica interna desse PhaseListener e, além disso, surgem sempre novas necessidades de configuração que implicam em novas programações do mecanismo, fazendo com que ele cresça demasiadamente tornando-o difícil de manter.

É importante observar que segurança é um assunto sério e não deve ser tratado por uma implementação qualquer, principalmente se for feita durante o desenvolvimento de um produto, pois o tempo do projeto vai estar andando e muitas vezes a implementação não estava agendada no cronograma, fazendo com que exista pressão para conclusão da atividade.

Solução

É necessário usar o Spring ou JBoss Seam na minha aplicação JSF?

Uma solução adequada para segurança seria usar um mecanismo robus-to, como o Spring Security para aplicações que usam o Spring, ou então, as extensões de segurança do JBoss Seam para aplicações que o usam como container. Ambas as soluções cobrem autenticação e autorização e são bastante extensíveis, além de que é bem mais simples programar extensões para o Spring Security ou o JBoss Seam que simplesmente acessem a base de regras da aplicação a ter que construir um mecanismo de segurança dentro da aplicação. Mais informações sobre segurança podem ser obtidas na edição nº. 28 da Mundoj, que traz o artigo “Os Sete Pecados do Controle de Acesso em Aplicações Java EE”.

Usar apenas a especificação JSF, como ela está hoje na versão 1.2, é pra-ticamente inviável para uma aplicação corporativa porque a limitação de recursos é muito grande: o número de componentes é altamente restrito e as características do Container são limitadas. Recursos como AJAX, te-mas e outros mais interessantes como AOP não existem na especificação, então haverá necessidade de complementar a tecnologia JSF com algo a mais, que pode ser o Spring ou JBoss Seam, dependendo das neces-sidades do projeto. Ambos têm um container poderoso e é bem mais

Um PhaseListener é uma interface que pode ser implementada por algum mecanismo que deseja receber notificações do Container antes e após a execução de cada fase do ciclo de vida. Uma ou mais implementações de um PhaseListener podem ser configuradas no faces-config.xml. Por ser um mecanismo que recebe notificações de fases, é muito comum ver implementações de dentro de uma aplicação que usam PhaseListeners para controlar a segurança da aplicação. A Listagem 16 mostra um trecho de como seria um PhaseListener que controla a segurança.

<h:form> … <!-- subform do Tomahawk --> <t:subform id=”regiaoA”> <h:inputText required=”true” value=”#{bean.name}” /> </t:subform> <t:subform id=”regiaoB”> <!-- É obrigatório, afinal é necessário para o cálculo. --> <h:inputText required=”true” value=”#{bean.date}”> <f:convertDateTime pattern=”dd/MM/yyyy”/> </h:inputText> <!-- Botão não precisa mais ser imediato. Não influencia regiões A e C --> <h:commandButton value=”Calcular Valor” action=”...”/> </t:subform> <t:subform id=”regiaoC”> <h:inputText required=”true” … /> </t:subform> ...</h:form>

Listagem 14. Página com componentes separados por Subforms.

public class Bean { //Sem Bindings private Data date; //Evento não-imediato. public void calcValue( ActionEvent evt ){ /** * Com subforms, a necessidade de bindings e eventos imediatos diminui ou * reduz a zero, permitindo que o código fique muito mais simples. */ if ( date.after( otherDate ) ) { //realiza a ação } }}

Listagem 15. Código de um Managed Bean que processa uma região marcada por subforms.

public class LoginPhaseListener implements PhaseListener { //on RESTORE_VIEW public void afterPhase(PhaseEvent e) { if( !isLoggedIn() && !isLogin() ){ //navigate to login page } }}

Listagem 16. Exemplo de um PhaseListener que controla segurança.

Page 10: Os 10 Maus Hábitos dos Desenvolvedores JSF · começaram a fazer suas primeiras ... ciclo de vida do JSF toma conta de cada instância ... propriedade visible do objeto está com

47

— Paginação de registros na sessão

-

-

-

-

Paginação é um processo para dividir um conteúdo com muitos da-dos em várias páginas menores. Com isso, ganha-se em usabilidade, pois é impossível para um usuário visualizar milhares de dados de uma vez só, e em desempenho, já que a aplicação lida com um nú-mero pequeno de informações a cada vez.

Alguns conjuntos de componentes para JSF, como o Trinidad e o RichFaces, fornecem componentes que fazem a paginação de regis-tros com algumas poucas linhas de código e isto é algo realmente simples para quem está começando. Basta uma lista de registros (muitas vezes entidades) e pronto, já se tem uma tabela com pa-ginação capaz de usar AJAX sendo exibida ao usuário em questões de minutos.

É importante observar que a paginação que estes componentes re-alizam é apenas uma paginação visual, ou seja, renderizam apenas um intervalo determinado de dados. Estes componentes não geren-ciam os dados, apenas os exibem em intervalos. Mesmo assim, essa

Durante o desenvolvimento, esta abordagem com paginação em

memória não aparenta ser um problema, porém quando a aplicação

vai para produção e o número de usuários simultâneos cresce, cada

vez mais dados vão parar na sessão, fazendo com que a memória

do servidor fique cheia de dados que não estão sendo usados. O

problema só tende a piorar com o tempo, já que o número de regis-

tros no banco de dados tende a crescer também. Um das melhores

maneiras de “matar” a escalabilidade da aplicação é a utilização

indiscriminada da sessão.

vantajoso que o Container do JSF seja complementado pelo container de uma dessas soluções.

Solução

Independentemente do framework Web, a melhor solução hoje

em dia para este tipo de problema é paginação sob demanda.

Com a paginação sob demanda, a aplicação somente recuperaria

os dados necessários para visualização do usuário. Portanto, se

existe um resultado de uma consulta com 40 mil registros e um

componente que apresenta 15 registros por página para o usuário,

não há motivos de trazer todos os 40 mil registros para memória.

Simplesmente, a cada página, o componente recuperaria apenas

15 registros do banco de dados e, durante a navegação entre as

páginas, efetuaria novas consultas para recuperar os 15 próximos

ou os 15 registros anteriores do banco de dados. Utilizar uma so-

lução como paginação sob demanda é essencial para o bom fun-

cionamento de um sistema e para o próprio servidor de aplicação.

O JBoss Seam, com o seu Seam Application Framework, já fornece

uma solução pronta para paginação sob demanda. Já em blogs,

fóruns e listas de discussão é fácil encontrar outras soluções, algu-

mas bem elaboradas e outras nem tanto. Contudo, a mais comum

e mais prática, sem dúvida, é estender a API do JSF, mais especi-

ficamente a classe javax.faces.model.DataModel, e implementar o

conceito de paginação sob demanda. Um exemplo pode ser visto

na Listagem 18.

facilidade de exibir dados em intervalos faz com que os desenvol-vedores fiquem tentados a jogarem os dados no escopo de sessão e se darem por satisfeitos com a paginação visual dos componentes.

Com certeza este é um dos maus hábitos mais recorrentes no desen-volvimento em JSF. Um exemplo de componente de paginação do Richfaces pode ser visto na Listagem 17.

<!-- o atributo rows define a quantidade registros exibidos por página -->

<rich:dataTable id=”tbl” rows=”10” value=”#{bean.listaDeProdutos}”

var=”produto”>

<!-- Colunas da tabela -->

</rich:dataTable>

<!-- Componente que controla a mudança de página para a tabela -->

<rich:datascroller for=” tbl” maxPage=”20” />

Listagem 17. Exemplo de paginação com o componente rich:datascroller do Richfaces.

Page 11: Os 10 Maus Hábitos dos Desenvolvedores JSF · começaram a fazer suas primeiras ... ciclo de vida do JSF toma conta de cada instância ... propriedade visible do objeto está com

48 www.mundoj.com.br

O código da Listagem 18 não está otimizado e é apresentado aqui apenas para fins de didática. Para a paginação sob demanda funcionar corretamente é necessário saber, antes de sair paginando, quantos re-gistros retornam em uma consulta. Essa informação é importante para que o componente de paginação consiga calcular quantas páginas serão exibidas. A DataTable irá usar o método getRowCount do DataModel para saber sobre essa informação. Após isso, a DataTable irá chamar três métodos do DataModel consecutivamente, setRowIndex, isRowAvaila-ble e getRowData. O método setRowIndex será usado para informar ao DataModel que índice a DataTable está tentando acessar. Logo depois, a DataTable vai chamar o método isRowAvailable para saber se aquele ín-dice existe nos dados do DataModel. Se o retorno for true, a DataTable vai finalmente chamar o getRowData. Dentro do método getRowData é que acontece a lógica da paginação sob demanda. A implementação pode verificar se o item requisitado pela DataTable está em cache e, se tiver, retorná-lo. Se o item não tiver em cache, o DataModel consulta o item (ou um intervalo deles) no banco de dados e armazena em cache antes de retorná-lo. Por último, basta associar a implementação do DataMo-del, chamada neste exemplo de DynamicDataModel, com a DataTable. O não uso de paginação sob demanda em projetos sérios pode trazer problemas de escalabilidade, desempenho ou até mesmo a inviabilidade do uso da aplicação.

— Realizar consultas em propriedades de managed beans expostas a componentes

O mais lógico e tentador, quando se tem esse cenário, é implemen-tar a chamada a uma consulta (que retorne uma lista de dados) dentro do método ‘get’ de um Managed Bean, como mostrado na Listagem 20. O que pode parecer uma solução simples se torna um problema. Os componentes acabam precisando dos dados mais de uma vez durante uma requisição e isso faz com a EL associada seja avaliada sempre que necessário. Essas inúmeras avaliações da EL provocam várias chamadas ao método do Managed Bean e que, por sua vez, também disparam a consulta várias vezes. Esse erro pode ser percebido através dos logs da aplicação, pois o registro da con-sulta sempre vai aparecer repetido. Disparar consultas em métodos ‘get’ é prejudicial ao desempenho da aplicação.

É bastante comum, para uma aplicação corporativa, exibir dados em combos ou tabelas. Em JSF, os componentes destinados a esta tarefa são, respectivamente, os componentes de seleção (ex.: classes que estendem UISelectOne e UISelectMany) e a DataTable (ex.: classe que estende UIData). O que esses componentes têm em comum é a possibilidade de se trabalhar com listas de objetos e, em JSF, esses dados podem ser fornecidos aos componentes através de Expression Languages (ELs) que, quando avaliadas, re-tornam os dados que serão exibidos pelos próprios componentes. Na maioria das vezes, a expressão avalia para uma propriedade de uma Managed Bean. A Listagem 19 mostra uma DataTable sendo associada a uma Value Expression. O problema não está na asso-ciação de uma expressão ao componente, mas sim na forma como os desenvolvedores suprem a lista de dados aos componentes.

public class DynamicDataModel extends DataModel {

//Método chamado pela Datatable para obter cada item da lista

public Object getRowData( ) {

//índice atual que a Datatable está acessando

int index = getRowIndex();

//verifica se o item está em cache

if ( ! cacheMap.containsKey( index ) ) {

//busca novos dados no banco de dados

Object newObject = fetchNewData(index);

cacheMap.put( index, newObject );

}

return cacheMap.get(index);

}

//retorna a quantidade de registros previamente

//determinada para a consulta

public in getRowCount(){

return count;

}

//Usado pela DataTable para saber se um registro está disponível.

public boolean isRowAvailable(){

return getRowIndex() >= 0 && getRowIndex() < count;

}

}

<!-- tabela associada a uma lista de usuários -->

<h:dataTable value=”#{bean.usersList}” var=”user”>

<h:column ...>

...

</h:column>

</h:dataTable>

public class Bean{

//Retorna uma lista de objetos

public List<Users> getUsersList(){

return service.findAllUsers();

}

}

Listagem 18. Exemplo de implementação do DataModel para paginação sob demanda.

Listagem 19. Associação de um valor a um componente via Value Expres-sion.

Listagem 20. Mau hábito — Uso de consultas em métodos 'get' de Managed Beans.

Page 12: Os 10 Maus Hábitos dos Desenvolvedores JSF · começaram a fazer suas primeiras ... ciclo de vida do JSF toma conta de cada instância ... propriedade visible do objeto está com

49

Solução

A solução para este tipo de cenário é simples: o método que realiza a consulta deve ser movido para dentro de uma callback ou para dentro de um evento, dependendo do momento que aquele dado será utilizado. Se o dado for utilizado logo ao entrar em uma página, a consulta pode ser levada para um método com a anotação @Pos-tConstruct, como mostrado na Listagem 21. A anotação @PostCons-truct marca um método para ser chamado logo após a inicialização de um managed bean, mas antes dele ser disponibilizado para uso. A vantagem da anotação @PostConstruct é que o método marcado só será executado apenas uma vez na inicialização do bean, evitan-do a chamada repetida.

Como o @PostConstruct é voltado para inicialização de beans, nem sem-pre será possível usar consultas que dependem da interação do usuário. Para esses casos, um evento é mais indicado. Outro cenário em que o evento se adequa mais é quando o dado só for requerido a partir de um determinado passo (ou página) de um caso de uso. A vantagem de se usar o evento é que a consulta, além de ser disparada uma única vez, só será chamada sob demanda. Um exemplo de como usar um evento para disparar consultas é mostrado na Listagem 22.

ciclo de vida

Figura 2. Fases do ciclo de vida da tecnologia JSF.

Uma das coisas mais importantes para quem desenvolve aplicações em JSF é entender o ciclo de vida de uma requisição. Conhecer apenas a documentação dos componentes não é suficiente para aproveitar o máximo de JSF, pois é entendendo o ciclo de vida que se compreende a ordem dos eventos e do processamento dos valores dos componentes, além de ter respostas a perguntas rotineiras como “porque um evento não está sendo chamado?”, “porque um componente está sem o valor?” ou “porque os valores não estão nas propriedades dos managed beans?”.

O ciclo de vida em JSF é dividido em seis fases, como ilustrado na figura 2, que são: Restore View, Apply Request Values, Process Validations, Update Model Values, Invoke Application e Render Response. Cada uma dessas fases será comentada nos tópicos a seguir.

Restore View

Apply Request Values

A primeira fase é responsável por construir uma árvore de componen-tes que representa a página que está sendo acessada. Essa construção acontece de duas formas: se for o primeiro acesso àquela página, os componentes serão instanciados sob responsabilidade das suas tags e, em seguida, a resposta é imediatamente gerada, ou seja, vai para a fase Render Response; se a requisição for um postback, significa que a árvore de componentes já foi criada em uma requisição anterior, então o processo restaura a árvore, faz a associação com o FacesContext e o fluxo parte para a segunda fase, a Apply Request Values.

Após a restauração da árvore de componentes, cada componente será responsável por obter seu novo valor através dos parâmetros enviados pela requisição HTTP. Esse valor será armazenado em cache, pois se ocorrer problemas com a validação ou conversão, esse valor será reexibido mais adiante.

Para os componentes que possuem o atributo immediate com o valor true, a conversão, validação e seus eventos serão processados nesta fase. Com-ponentes que implementam EditableValueHolder (inputs, selects) execu-tarão o processo da fase Process Validations neste momento. É incorreto afirmar que componentes imediatos pulam a etapa de validação. O correto é afirmar que o processamento deles é antecipado para a segunda fase.

Action Events

Immediate Events

Value Change Events

Apply Request Values

Render Response

Invoke Application

Update Model Values

Restore ViewREQUEST

RESPONSE

Process Validations

public class Bean{

@PostConstruct

public void initialize(){

//só é chamado uma vez

this.users = service.findAllUsers();

}

public List<Users> getUsersList(){

return this.users;

}

}

Listagem 21. Uso do @PostConstruct para efetuar consultas na iniciali-zação do bean.

public class Bean{

public void search(ActionEvent evt){

this.users = service.findUsers(parameters);

}

public List<Users> getUsersList(){

return this.users;

}

}

Listagem 22. Uso de eventos para efetuar consultas sob demanda do usuário.

Page 13: Os 10 Maus Hábitos dos Desenvolvedores JSF · começaram a fazer suas primeiras ... ciclo de vida do JSF toma conta de cada instância ... propriedade visible do objeto está com

50

Process Validations

Update Model Values

Invoke Application

Render Response

A terceira fase é responsável por disparar o processo de validação de cada componente. É nesse momento que o componente deve verificar, junto com os validadores, se o valor que foi armazenado na Apply Request Va-lues é correto. Se ocorrer falha de validação, os validadores adicionam uma mensagem de erro ao FacesContext e sinalizam ao Container que o fluxo não deve seguir para a próxima fase, mas sim para a Render Response.

A validação em JSF sempre é precedida pela conversão do valor, seja na Apply Request Values ou na Process Validations. Em outras palavras, os validadores dos componentes sempre validam o valor convertido para o modelo da aplicação e nunca o que está vindo na requisição HTTP (sempre em String).

Se o processo de validação for bem-sucedido, significa que os compo-nentes podem atualizar as propriedades dos Managed Beans que estive-rem associadas aos seus valores (via Value Expression).

É nesta fase que o evento de um botão ou link que submeteu o formulário será chamado, exceto se tiver sido marcado como imediato. Neste momento, é seguro executar a lógica da aplicação porque os valores já passaram por conversão e validação.

A partir do método que processa esse evento, um Managed Bean pode su-gerir qual será a próxima página a ser renderizada retornando um outcome, que é uma String que será usada para localizar uma regra de navegação.

Considerações finais

Como se pode ver, a maioria dos maus hábitos apresentados neste artigo está intimamente ligada à carência ou ao não conhecimento dos desen-volvedores sobre o Ciclo de Vida do JSF e da arquitetura de componentes. Entender o Ciclo de Vida é obrigação de qualquer profissional que tenha a intenção de trabalhar com a tecnologia, bem como ter o mínimo de conhecimento sobre desenvolvimento Web. Dessa forma, a maioria dos maus hábitos discutidos aqui podem ser evitados. Vale ressaltar também que é importante que um arquiteto ou um desenvolvedor experiente em JSF esteja por perto para orientar uma equipe que esteja iniciando nessa tecnologia.

Possuir alguns destes maus hábitos é algo completamente normal para alguém que está começando a desenvolver com JSF, principalmente se este alguém tiver vindo de um paradigma action-based. De fato, a mu-dança de paradigma nem sempre será um passo relativamente fácil, mas o grande objetivo deste artigo foi facilitar essa adaptação para os novos desenvolvedores.

A dificuldade na mudança da filosofia action-based para component-ba-sed não difere tanto assim na dificuldade da mudança da programação procedural para programação orientada a objetos; ou do desenvolvi-mento Desktop para o desenvolvimento Web. Conhecer o “caminho das pedras” requer tempo, estudo e experiência, e obviamente que isso não acontece da noite para o dia.

Referências

http://java.sun.com/products/jsp/jstl/

http://java.sun.com/products/jsp/reference/techart/unifiedEL.html

http://balusc.blogspot.com/2007/03/post-redirect-get-pattern.html

http://www.jcp.org/en/jsr/detail?id=252

A última fase é responsável por gerar a resposta e persistir o estado da árvore de componentes para ser usada em próximas requisições. A persistência do estado da árvore pode ser no cliente ou no servidor dependendo da escolha da aplicação.

www.mundoj.com.br