Índice
O JavaServer Faces \351 um framework web baseado em componentes de interface gr\341fica. Mais do que componentes, o Faces fornece uma s\351rie de mecanismos para convers\343o, valida\347\343o, execu\347\343o de l\363gica de neg\363cios e controle de navega\347\343o.
Por ser baseado em componentes, o framework possui um workflow diferenciado do modelo tradicional de aplica\347\365es web, e conhecer o ciclo de vida JSF passa a ser essencial para p\341ginas com interfaces gr\341ficas mais complexas.
Temos ainda diversos outros frameworks complementares que auxiliam o desenvolvedor para prop\363sitos variados, como \351 o caso do RichFaces para constru\347\343o de aplica\347\365es em AJAX, do Facelets para cria\347\343o de templates, ou do JBoss Seam para integra\347\343o de camadas.
O JavaServer Faces \351 o framework para desenvolvimento de aplica\347\365es web padr\343o da Java EE. Ele \351 mantido pela Java Community Process JSR-314, que define o padr\343o para desenvolvimento de interfaces dentro do modelo orientado a componentes. Essa caracter\355stica \351, na maioria das vezes, o maior obst\341culo para o aprendizado da maioria dos desenvolvedores, principalmente os que conhecem algum framework baseado em a\347\365es, como por exemplo, Struts.
A arquitetura JSF favorece o surgimento de ferramentas RAD (desenvolvimento r\341pido de aplica\347\365es) atrav\351s da arquitetura de componentes, da infra-estrutura da aplica\347\343o e do conjunto de componentes padr\365es. Para a IDE Eclipse, h\341 diversos plugins, como o WTP, o JBoss Tools e MyEclipse.
J\341 o NetBeans oferece um suporte avan\347ado com recursos de drag-and-drop para montagem de p\341ginas, al\351m de oferecer componentes mais avan\347ados que os padr\365es, cabe ressaltar que estes recursos est\343o atrelados a um modelo de desenvolvimento que o usu\341rio deve seguir, caso contr\341rio os recursos s\343o bem limitados.
Os componentes JSF s\343o orientados a eventos, ou seja, \351 poss\355vel processar eventos gerados pelo cliente, como um clique no bot\343o ou altera\347\343o do valor de campo texto. Por padr\343o, h\341 um conjunto de componentes b\341sicos para manipula\347\343o de formul\341rios, mas o Faces oferece uma infra-estrutura para cria\347\343o de novos componentes. Dentre os componentes de terceiros, os mais conhecidos s\343o o JBoss RichFaces e Apache Tomahawk . O presente artigo objetiva mostrar uma vis\343o geral sobre as principais caracter\355sticas do JSF, as quais ajudar\343o o leitor que est\341 iniciando o trabalho com o framework e tamb\351m aquele que j\341 trabalha com ele.
Todos os conceitos abordados ser\343o baseados na vers\343o 1.2, a vers\343o oficial na data em que o artigo foi escrito. Vamos iniciar analisando o ciclo de vida do <span style="font-style: italic;">Faces</span>, que \351 essencial para desenvolver interfaces mais complexas e resolver problemas que s\343o comuns durante o desenvolvimento de uma aplica\347\343o.
Toda requisi\347\343o realizada dentro do contexto JSF passa por um processo de seis fases, conhecido como ciclo de vida JSF. Em suma, durante esse processo \351 restaurada a \341rvore de componentes, os valores s\343o lidos, convertidos e validados; eventos s\343o executados e uma resposta \351 gerada para o usu\341rio. Os eventos s\343o executados, na grande maioria, ap\363s uma dessas seis fases.
A Figura 1 mostra o fluxo de execu\347\343o de uma requisi\347\343o gerada pelo cliente pelo ciclo de vida JSF. O processo inicia-se assim que a requisi\347\343o \351 recebida pelo servlet do JSF. \311 importante lembrar que o JSF \351 constru\355do em cima da API do Servlet.
A requisi\347\343o \351 recebida pelo FacesController, que extrai a View ID usada para identificar a p\341gina JSP associada a ela. Uma View \351 a representa\347\343o de todos os componentes que comp\365em uma determinada p\341gina. Uma vez que a p\341gina est\341 em m\343os, \351 realizada uma tentativa de restaura\347\343o desta View que, geralmente, \351 restaurada com base em um campo oculto, ou baseada na sess\343o de usu\341rio.
Figura 1 - Ciclo de vida JSF
A \341rvore de componentes da View \351 obtida atrav\351s de duas formas: Initial View e Postback, que s\343o executados de maneiras distintas.
A Initial View ocorre quando a p\341gina \351 acessada pela primeira vez ou quando a p\341gina \351 acessada via HTTP GET. O contexto JSF cria a \341rvore de componentes, com base na View acessada, e a mesma \351 gravada na propriedade viewRoot do objeto FacesContext. Esse objeto cont\351m todas as informa\347\365es relacionadas \340 requisi\347\343o que s\343o necess\341rias para manipular o estado dos componentes GUI de uma p\341gina de uma determinada requisi\347\343o. Como n\343o h\341 nenhum valor de entrada a ser processado, o fluxo avan\347a para a \372ltima fase, a Render Response.
J\341 o Postback ocorre quando o usu\341rio interage com o formul\341rio da p\341gina, seja alterando um valor de um campo de texto ou clicando num link ou bot\343o. Nesses casos, a requisi\347\343o \351 enviada para a mesma p\341gina da qual foi submetido o formul\341rio.
Como a View j\341 existe, ela precisa ser restaurada; nesse caso, o JSF utiliza as informa\347\365es restauradas para reconstru\347\343o do estado da View. Uma vez reconstru\355do o estado, o fluxo passa para a pr\363xima fase do ciclo de vida.
Tamb\351m conhecido como processo de decodifica\347\343o, a fase Apply Request Values \351 respons\341vel por atribuir aos componentes o valor submetido atrav\351s de par\342metros enviados no request. Todo componente que aceita a entrada de valores possui uma propriedade que recebe o valor original enviado pelo usu\341rio. Esse valor \351 obtido com base no ID do componente. Um componente pode ser renderizado mais de uma vez, geralmente em virtude de uma intera\347\343o. Para garantir um ID \372nico para cada componente, o JSF adiciona como prefixo o ID do componente pai, o ID resultante \351 conhecido como Client Id.
Por exemplo, o Client Id de uma caixa de texto com ID \u201cnome\u201d dentro de um formul\341rio com ID \u201ccadastro\u201d ser\341 \u201ccadastro:nome\u201d.
Todo componente possui uma propriedade immediate que indica se o valor deve ser convertido e validado nesta fase. J\341 em componentes do tipo comando, como um link ou bot\343o, essa propriedade \351 usada para ignorar todos os valores do formul\341rio. Voc\352 utilizar\341 esta op\347\343o para bot\365es cancelar ou para links que aceitam valores de um controle espec\355fico.
Conclu\355da a decodifica\347\343o, \351 verificado se houve algum erro de valida\347\343o ou convers\343o. Caso exista algum erro, o ciclo avan\347a para a fase Render Response; caso contr\341rio, o ciclo avan\347a para a pr\363xima fase.
Nesta fase \351 assegurado que todos os valores enviados s\343o v\341lidos.
A valida\347\343o \351 desempenhada diretamente pelo componente e tamb\351m pode ser delegada para um ou mais validadores. Antes disso, o valor submetido pelo usu\341rio precisa ser convertido, o que \351 feito por meio de um conversor padr\343o ou atrav\351s de um conversor espec\355fico - mais adiante veremos como implementar um conversor JSF.
Considerando o exemplo da Listagem 1, temos um campo de e-mail com valida\347\343o de obrigatoriedade, por meio da propriedade required, e com valida\347\343o espec\355fica por meio do validador jm.validator.email - a cria\347\343o de validadores \351 discutida mais adiante.
Primeiramente, \351 validado se o campo n\343o est\341 vazio; caso a valida\347\343o falhe, \351 mostrada a mensagem de obrigatoriedade para o campo e-mail customizada \u201c\311 necess\341rio informar seu e-mail\u201d. Ap\363s a valida\347\343o de obrigatoriedade, o valor submetido \351 convertido no tipo de dado da propriedade user.email, o qual \351 uma String.
Convertido o valor, a valida\347\343o \351 delegada aos validadores registrados para o componente, neste caso, o validador com ID jm.validator.email ser\341 chamado.
Listagem 1. Exemplo de valida\347\343o de campos
1 <h:inputText id="email" value="#{user.email}" size="50" required="true" requiredMessage= "\311 necess\341rio informar seu e-mail"> 2 <f:validator validatorId="jm.validator.email"/> 3 </h:inputText> 4 <h:message for="email" />
Uma vez validado o valor enviado pelo usu\341rio, o mesmo \351 verificado com o valor do componente; caso seja diferente, \351 gerado um evento de altera\347\343o de valor. Nesse ponto, eventos value-change e qualquer outro evento associado a esta fase s\343o executados pelos listeners apropriados.
Caso todos os valores submetidos sejam v\341lidos, a execu\347\343o passa para a pr\363xima fase do ciclo; em caso de erros, a p\341gina \351 renderizada com as mensagens de valida\347\343o.
Agora temos os valores enviados atualizados, convertidos nos tipos de dados desejados e validados. Ent\343o, \351 o momento ideal para que estes valores sejam associados aos objetos de modelo e aos Backing Beans. Observe a express\343o #{user.email} da propriedade value no campo e-mail da Listagem 1. Para este exemplo, o JSF ir\341 procurar uma inst\342ncia de user nos contextos Servlets: request, session e application.
Uma vez encontrada a inst\342ncia, o componente atribui o valor \340 propriedade do componente, neste exemplo, na propriedade email.
Os termos Managed Beans e Backing Beans s\343o usados frequentemente para fazer refer\352ncias \340s classes controladoras de uma aplica\347\343o JSF. Por\351m, h\341 uma diferen\347a entre eles: Managed Beans s\343o JavaBeans registrados no contexto JSF que podem ser acessados via Expression Language em p\341ginas JSP; j\341 os Backing Beans t\352m as mesmas caracter\355sticas dos Managed Beans com a adi\347\343o de propriedades vinculando componentes de uma p\341gina JSP ao Bean.
Como voc\352 deve ter percebido, at\351 este momento do ciclo de vida os valores submetidos do formul\341rio j\341 foram atualizados, convertidos, validados e associados \340s respectivas propriedades dos Backing Beans e tudo isso sem qualquer c\363digo de aplica\347\343o.
Essa \351 a grande vantagem de se usar Faces: o desenvolvedor foca apenas na implementa\347\343o das l\363gicas de neg\363cio, e as tarefas repetitivas e tediosas s\343o executadas automaticamente. \311 claro que em alguns casos ser\341 necess\341ria a interven\347\343o de voc\352, desenvolvedor, mas ser\343o implementa\347\365es pontuais, ou seja, um conversor para um tipo espec\355fico de dados, um validador para um conjunto de regras, etc.
Completada essa fase, a execu\347\343o passa para a pr\363xima fase, respons\341vel por chamar qualquer evento ou a\347\343o registrados para a requisi\347\343o.
Neste momento, a aplica\347\343o tem o estado necess\341rio para executar todos os eventos e l\363gicas de neg\363cios da aplica\347\343o. H\341 dois tipos de m\351todos que podem ser chamados neste momento: action handlers e event listeners.
Action handlers s\343o usados para controle de pagina\347\343o. Nesse contexto, dependendo da l\363gica de neg\363cio processada, \351 definida a qual p\341gina o usu\341rio deve ser redirecionado. Um action handler \351 um m\351todo de um Backing Bean sem argumentos que retorna uma string que determinar\341 a p\341gina de destino. Por ser um m\351todo sem argumentos, o action handler n\343o cont\351m a informa\347\343o de qual componente chamou a a\347\343o.
Por outro lado, o event listener n\343o tem retorno e recebe como par\342metro um objeto do tipo ActionEvent, que cont\351m informa\347\365es relacionadas ao evento, como o componente que o originou, a phaseId relacionada ao ciclo de Vida JSF. Por n\343o ter retorno, este tipo de a\347\343o \351 ideal para executar l\363gicas de neg\363cios quando o usu\341rio clica em um bot\343o e um campo \351 atualizado, ou selecionando uma op\347\343o de uma caixa de sele\347\343o, que determina a exibi\347\343o de valores adicionais.
Os m\351todos listener podem ser criados dentro de Backing Beans ou em classes separadas, mas h\341 uma vantagem em se criar classes separadas que \351 a possibilidade de reutiliza\347\343o em mais de uma p\341gina.
\311 importante notar aqui a ordem que os elementos s\343o processados, ou seja, onde o c\363digo da sua aplica\347\343o \351 executado. Nesse cen\341rio, voc\352 n\343o precisa, a menos que em casos espec\355ficos, de se preocupar com o request do usu\341rio. Ap\363s todas as a\347\365es serem executadas, chegou o momento de gerar o resultado ao usu\341rio.
Esta \351 a \372ltima fase do ciclo de vida JSF, todo o processamento no n\355vel de framework e no n\355vel da aplica\347\343o j\341 foi realizado. Essa \372ltima fase tem dois objetivos: o primeiro \351 gerar e enviar a resposta para o usu\341rio e o segundo \351 salvar o estado da View para ser restaurada no pr\363ximo request, caso a p\341gina venha a requisit\341-la novamente.
Durante a renderiza\347\343o dos componentes, os conversores s\343o novamente chamados para transformar o objeto em uma String para ser visualizada.
Depois de completada a fase, o container web transmite fisicamente a resposta para o usu\341rio, a qual \351 exibida no navegador.
Agora que n\363s j\341 conhecemos como o Faces trabalha, vamos codificar o cadastro de clientes de uma loja virtual de livros.
Para o desenvolvimento do nosso projeto Faces, vamos utilizar a IDE Eclipse Ganymede com o plugin JBoss Tools 3, o container web Tomcat 6, JSF e JSTL 1.2 e a JPA (ver Links).
O objetivo deste artigo \351 mostrar em detalhes o funcionamento do JSF, por isso n\343o iremos utilizar os wizards do JBoss Tools para a cria\347\343o do projeto, pois iremos configur\341-lo manualmente.
O primeiro passo \351 criar um Dynamic Web Project no eclipse com o nome \u201cLivrosOnline\u201d.
Ap\363s criado o projeto, copie as bibliotecas do JSF 1.2 (jsf-api.jar e jsf-impl.jar) e as bibliotecas JSTL (jstl-api.jar e jstl-impl.jar) para pasta WebContent/WEB-INF/lib do projeto.
Copie tamb\351m as bibliotecas JPA (hibernate-entitymanager.jar, todos os .jar do diret\363rio lib e lib/test). A estrutura do projeto ap\363s a c\363pia das bibliotecas \351 semelhante \340 Figura 2.
O pr\363ximo passo \351 configurarmos o descritor web da aplica\347\343o conforme a Listagem 2.Nele temos que declarar o servlet listener e o servlet do JSF. O servlet listener ConfigureListener \351 respons\341vel por analisar todos os arquivos de configura\347\365es relevantes para a aplica\347\343o e configurar o ambiente de execu\347\343o da implementa\347\343o JSF.
J\341 o FacesServlet \351 respons\341vel por gerenciar o ciclo de processamento das requisi\347\365es para construir a interface de usu\341rio baseado nos componentes JSF. Note, tamb\351m, que foi adicionado o par\342metro STATE_SAVING_METHOD, que determina onde ser\341 salvo o estado da \341rvore de componentes da View.
Neste exemplo, ser\343o salvos no lado do cliente, ou seja, ser\341 criada uma propriedade oculta dentro do formul\341rio que armazenar\341 o estado no formato hexadecimal. Semelhante \340 maioria de outros frameworks, o Faces tamb\351m possui um arquivo de configura\347\365es, o faces-config.xml.