a renovação do jsf
TRANSCRIPT
A renovação do JSF – Parte 1
Esta é a primeira parte de uma série de dois artigos que, juntos,
trarão ao leitor uma avaliação das principais características
incorporadas na versão mais recente da especificação JavaServer
Faces, integrada à Java EE 7.
Esta é a primeira parte de uma série de dois artigos que, juntos, trarão ao leitor uma avaliação das principais
características incorporadas na versão mais recente da especificação JavaServer Faces, integrada à Java EE
7. Embora a grande maioria das mudanças tenha um caráter mais corretivo ou adaptativo, outras constituem
um arsenal ainda maior e mais rico de recursos para o desenvolvimento de soluções web em Java. Os pontos
abordados ao longo da série não visam, definitivamente, encerrar o assunto, que é muito amplo e tem uma
gama incrível de aplicações. Deste modo, nosso foco será direcionado quase que totalmente para quatro das
mais significativas características, as chamadas Big Ticket Features.
Em que situação o tema é útil
Este tema é bastante relevante para todo desenvolvedor que use a tecnologia Java como alvo em seus
sistemas. É igualmente útil para todo entusiasta de tecnologias web e que, por este motivo, gosta de se
manter atualizado em relação ao modo como os frameworks mais populares do mercado evoluem ao longo
do tempo.
Precisamente no dia 12 de junho de 2013, a Oracle lançou oficialmente a versão 7 da Java Enterprise
Edition, a plataforma Java para desenvolvimento de soluções web. Esta edição é mais evolucionária do que
revolucionária, e conta com atualizações importantes em praticamente todas as especificações que a
compõem (Servlets, JPA, Expression Language, CDI, EJB, JMS, JTA, JSP e outras) e também com o
lançamento de outras inéditas, como a API para o protocolo Web Sockets (JSR-356) e a API para
processamento de conteúdo JSON (JSR-353). Além disso, a Oracle liberou sua implementação de referência
para essas revisões através de uma nova versão de seu servidor de aplicações, o GlassFish 4, bem como uma
atualização da IDE NetBeans para a versão 7.3.1, totalmente compatível com a Java EE 7 e disponível para
download gratuito no site do projeto (veja o endereço para a página desta IDE na seção Links).
Neste artigo, cobriremos as novidades em torno da especificação JSF, dando maior atenção para as
características consideradas mais profundas, intituladas pelo próprio grupo de trabalho como Big Ticket
Features. Ao longo do texto, revisitaremos alguns conceitos fundamentais para facilitar a compreensão do
cenário em que a implementação atual se insere, complementando com exemplos práticos e análises passo a
passo de todo o código-fonte utilizado. Desta forma, esperamos trazer ao leitor um nível de conforto
suficiente para encorajá-lo a utilizar as novidades deste framework em suas próximas aplicações web ou,
ainda, aplicá-las nas já existentes.
Todo o trabalho realizado para o lançamento do JSF 2.2 originou-se no texto proposto na JSR-344. Boa
parte do que se planejou foi cumprido, e o resultado final é uma especificação ainda mais madura, completa,
poderosa e, por que não dizer, flexível. Entretanto, os atrasos foram inevitáveis, dada toda a turbulência
gerada em torno da tecnologia Java ao longo dos últimos meses por conta de algumas vulnerabilidades
tratadas com grande preocupação – e, cabe comentar, certo exagero – pela mídia. Tudo isso fez com que
parte do trabalho fosse iniciada tardiamente, culminando na redução do escopo de algumas características e,
em alguns casos, postergação de recursos para edições futuras.
A liderança das atividades em torno do JSF 2.2 ficou, mais uma vez, por conta do competente Ed Burns, um
engenheiro sênior da Oracle à frente do desenvolvimento desta especificação desde sua primeira versão. Na
seção Links, no final do artigo, convidamos o leitor a visitar a página da JSR-344, na qual é possível
conhecermos todos os demais membros da equipe e tudo aquilo que foi proposto e realizado por eles. Esta é
uma cultura importante, pois nos ajuda a conhecer melhor o papel fundamental exercido pelo JCP (Java
Community Process) na evolução da tecnologia Java em sua totalidade. À medida que analisamos o
potencial e o nível de influência das cabeças por trás de cada JSR (acrônimo para Java Specification
Request), bem como as empresas que, direta ou indiretamente, estão envolvidas nas discussões, passamos a
entender com muito mais clareza os motivos que fazem com que Java seja líder no mercado mundial por
tantos e tantos anos.
Embora as chamadas “Big Ticket Features” sejam poucas (mais precisamente, quatro), envolvem muitos
conceitos e imprimem, por este motivo, grande densidade ao tema. Logo, para que consigamos tratar todos
os pontos principais com o cuidado necessário e didática adequada, dedicaremos todo o restante do artigo
para a análise pontual de cada um deles, reservando o final do texto para algumas conclusões importantes
acerca de todo o material apresentado.
Algumas palavras iniciais sobre o JSF 2.2
JavaServer Faces, como sabemos, é uma proposta de framework para a construção da camada de
apresentação em sistemas web desenvolvidos em Java. Trata-se de uma dentre as muitas especificações que,
juntas, constituem a plataforma Java EE. Inúmeras tecnologias relacionam-se entre si neste universo, e o
JavaServer Faces 2.2 tem algumas dependências diretas que precisam ser conhecidas por todo
desenvolvedor, para trazer ao leitor uma visão da posição deste framework no ecossistema global. São elas:
· JavaServer Pages 2.2, incluindo Expression Language 2.2 (JSR-245);
· Expression Language 3.0 (JSR-341);
· Servlet 3.0 (JSR-315);
· Java Standard Edition, versão 6 (JSR-270);
· Java Enterprise Edition, versão 6 (JSR-316);
· Java Beans 1.0.1;
· JavaServer Pages Standard Tag Library (JSTL) 1.2.
Todas essas JSRs estão devidamente documentadas no site do JCP, livres para download e consulta.
Novamente, recomendamos ao leitor que dedique algum tempo para conhecer melhor este processo, que dita
o passo do progresso da tecnologia Java em seus diversos sabores.
Outro ponto que devemos destacar, antes de começar a investigar as novas características do framework, é a
limpeza realizada nos seus espaços de nomes (namespaces). O nome Sun foi definitivamente removido das
URIs, que foram redefinidas conforme o conteúdo da Tabela 1. Embora os namespaces antigos ainda sejam
válidos, a recomendação, desde já, é para que passemos a evitar seu uso, substituindo-os pelos novos.
Tabela 1. Mudança das URIs dos espaços de nome.
Agora que já falamos rapidamente sobre essas mudanças mais gerais da especificação, é hora de
começarmos a explorar suas principais características.
A primeira Big Ticket Feature: marcadores amigáveis à HTML
Antes de entrarmos no mérito de marcadores “HTML-friendly”, dedicaremos algumas palavras iniciais para
contextualizar o processo de tratamento de requisições de usuário desde o momento em que chegam ao
servidor até aquele em que uma resposta é enviada para o cliente. Observe o diagrama da Figura 1, que
retrata a máquina de estados à qual toda requisição JSF é submetida. Tudo começa em sua recepção, feita
por uma instância da classe javax.faces.webapp.FacesServlet. Este é o controlador frontal do framework,
por onde toda entrada (javax.servlet.ServletRequest) chega e toda resposta (genericamente representada
pela classe javax.servlet.ServletResponse) é despachada. A cada nova requisição, este controlador ficará
responsável por criar um novo objeto de contexto (javax.faces.context.FacesContext) ao qual todas as
estruturas relacionadas a ela (e seus respectivos estados) serão vinculadas. Logo em seguida, chega o
momento do ciclo de vida ser executado; representado genericamente pela classe
javax.faces.lifecycle.Lifecycle, ele encapsula o tratamento de cada um dos seis blocos ilustrados no
diagrama da Figura 1 da seguinte maneira:
· Lifecycle.execute(javax.faces.context.FacesContext): cuida dos cinco primeiros blocos do ciclo de vida,
da restauração da view à invocação da aplicação;
· Lifecycle.render(javax.faces.context.FacesContext): cuida da fase de renderização de respostas que
deverão ser enviadas ao cliente (normalmente, um navegador web);
Figura 1. Ciclo de vida de requisições JSF.
Observe que apenas a renderização é separada dos demais blocos do ciclo de vida, fato justificado pela
maior complexidade associada a esta fase. Nos dois métodos listados, vemos que há um parâmetro de
entrada que guarda uma instância de contexto, criada para o tratamento da requisição. Este objeto será, passo
a passo, recheado com todas as informações pertinentes à interação atual, de modo a representar de forma
fiel a situação da aplicação, a cada momento.
A primeira fase do ciclo de vida é a Restauração de uma view, que, em JSF, é representada genericamente
pela classe javax.faces.view.ViewDeclarationLanguage. Nesta fase, o controlador procurará por uma view
cujo identificador (view ID) corresponda exatamente àquele passado na requisição. Caso não a encontre, ela
precisará ser totalmente montada. Por outro lado, se for encontrada, será apenas restaurada. Em ambos os
casos, o objetivo desta fase é fazer com que a view associada à requisição esteja devidamente montada e
identificada no lado servidor.
A forma que o JSF usa para manter esta representação de uma view em memória é a de uma árvore de
objetos, que se inicia a partir de uma raiz (uma instância de javax.faces.component.UIViewRoot), na qual
todos os demais elementos (especializações de javax.faces.component.UIComponent) estão vinculados.
As quatro fases seguintes do ciclo de vida, descritas abaixo, serão executadas apenas nos casos em que a
requisição tenha dados a serem processados. Caso contrário, serão ignoradas e apenas a etapa de
Renderização de resposta será efetivamente processada. Um bom exemplo de cenário em que a resposta é
imediatamente renderizada depois da execução da restauração da view é aquele em que o usuário acessa o
sistema pela primeira vez e a tela inicial deve ser exibida. Neste caso, não há nenhum dado sensível vindo
do cliente e nenhuma ação disparada pelo mesmo; o que existe é apenas uma solicitação de inicialização do
sistema e nada mais.
A segunda etapa no ciclo de vida JSF é a Aplicação dos valores da request. Nela, cada componente da
árvore citada anteriormente terá uma chance de avaliar os parâmetros enviados pelo cliente na requisição,
identificar quais se relacionam com ele e, por fim, atualizar-se de modo a refletir exatamente o estado atual
da aplicação, guardando todos esses valores localmente.
Então, a máquina de estados é movida para a fase do Processamento de validações. Novamente, é dada
uma chance, para cada componente da view, de executar todas as conversões e validações a eles associadas.
Este é o momento em que a checagem de intervalos de dados e/ou conversões de tipos são realizadas, de
modo a assegurar que os dados preenchidos no cliente sejam válidos e representem exatamente o que devem
representar.
Se todos os valores preenchidos no cliente forem válidos e todas as conversões programadas forem bem
sucedidas, será permitido que a fase de Atualização dos valores de modelo seja executada. Até o momento
anterior, o foco estava todo na camada de apresentação, em que cada componente da view guardava
localmente os dados a ele associados. Neste instante, entretanto, já é seguro refletir na camada de modelo
todas as informações contidas na camada de apresentação. Para isso, faz-se uso de beans gerenciados (ou
backing beans), associados às views por meio da Linguagem de Expressão (EL, definida na JSR-341),
especificação que também compõe a Java EE e da qual o JSF faz uso massivo.
Até aqui, todos os dados preenchidos e selecionados na view já estão devidamente referenciados na camada
de modelo. A próxima fase, de Invocação da aplicação, consiste em dar aos componentes da view a chance
de executarem ações disparadas pelo cliente. Apenas para facilitar a visualização, imagine uma ação como
sendo um clique em um botão de submissão de formulário. Novamente, há aqui a possibilidade de um bean
gerenciado estar envolvido no processo, desde que métodos implementados por ele sejam vinculados a
propriedades de ação dos componentes desta view, também por meio da já citada EL. É nesta fase que,
através de regras de navegação mapeadas no sistema (mais especificamente, no arquivo de configurações
JSF, o popular faces-config.xml), define-se a próxima view a ser apresentada para o cliente, através da
configuração e utilização de outcomes.
A última fase do ciclo de vida JSF é a Renderização da resposta. Aqui, o sistema já conhece a view que
deve ser apresentada ao cliente, bem como todos os dados que devem ser vinculados a ela. Logo, é
necessário que cada objeto da árvore de componentes JSF seja devidamente convertido para uma linguagem
que seja legível para o cliente. Embora JSF possa ser estendido para trabalhar com outras linguagens de
marcação e outros protocolos, toda a sua implementação padrão está baseada em HTTP(s) e HTML. Quando
o processo de “tradução” é finalizado, o objeto de resposta é preparado e, por fim, enviado para o cliente
através do mesmo controlador frontal, FacesServlet, já mencionado no início do ciclo de vida.
Ao final da etapa de Renderização da resposta, o conteúdo criado é finalmente enviado ao cliente, através
do objeto de resposta (ServletResponse) gerado e manipulado diretamente pelo controlador frontal do
framework (FacesServlet).
Este é, portanto, o processo completo de tratamento de requisições de usuário. Mas, afinal, onde (e de que
maneira) se encaixa a Big Ticket feature intitulada HTML Friendly Markups?
O fato é que, desde a JSF 2.0, a linguagem padrão para declaração de views (View Declaration Language,
ou VDL) é a Facelets, substituta direta da velha conhecida tecnologia de todo programador web em Java, o
JSP (JavaServer Pages). Cada marcador escrito nesta VDL está associado a um componente do framework
(que será, por sua vez, uma especialização de javax.faces.component.UIComponent), e os processos de
encoding (tradução de HTML para componentes Java) e decoding (tradução de componentes Java para
código HTML) que ocorrem nas fases de Restauração de views e Renderização de respostas são
realizados por agentes conhecidos, respectivamente, como view handlers (no caso do encoding em nível de
views), tag handlers (no caso de encoding em nível de marcadores) e renderizadores. Todos eles são
acionados a partir da instância de javax.faces.view.ViewDeclarationLanguage que representa a view em
questão, como já dito. Páginas construídas com o uso de Facelets acabam obrigatoriamente vinculadas ao
processo de renderização do JSF. Sendo assim, para que se tenha uma ideia de como ela será exibida ao
usuário final, é necessário que implantemos esse sistema em um servidor de aplicações (como GlassFish,
WebSphere ou JBoss) ou um servidor web (como Jetty ou Tomcat) e o coloquemos em execução.
Esta nova característica trazida pelo JSF 2.2, no entanto, apresenta dois aspectos que nos permitem mudar
significativamente a forma de trabalhar a interface com o usuário: os atributos e os elementos “pass-
through”. Como os próprios nomes sugerem, esses elementos são ignorados pelo processo convencional do
JSF para a interpretação dos elementos de uma view, uma vez que eles não têm participação na fase de
renderização e serão úteis apenas no processamento de conteúdo, no ambiente servidor. Veremos, a seguir,
como cada um funciona.
Atributos pass-through
Todo marcador JSF possui um conjunto de atributos composto por uma combinação de propriedades
definidas no componente (javax.faces.component.UIComponent) e no renderizador (especialização de
javax.faces.render.Renderer) para ele projetados. Conforme já vimos no ciclo de vida acima, cada
elemento da view tem a chance de ler e processar parâmetros escritos na requisição que chega ao servidor.
Para facilitar a visualização disso, basta que imaginemos um elemento muito comum em páginas web: uma
caixa de texto. Enquanto uma palavra digitada é uma propriedade do componente (com valor de negócio,
muitas vezes, sendo usada para atualização do modelo em fases mais avançadas do ciclo de vida), seu
tamanho em pixels é algo mais relacionado à forma como ela deverá ser desenhada para o usuário. Em
ambos os casos, tanto o renderizador quanto o componente já “sabem”, de antemão, exatamente quais os
dados que devem procurar em cada requisição que chega, pois se trata de algo que pertence ao projeto
dessas classes.
No entanto, atributos pass-through têm um propósito diferente: permitir ao programador que defina atributos
adicionais, arbitrários ou não, que serão ignorados tanto pelo componente quanto pelo renderizador a ele
associado. Para exemplificar, vejamos o código da Listagem 1. Nela, vemos as três formas possíveis de se
trabalhar com atributos desta natureza, que são:
1. Por meio do emprego do marcador f:passThroughAttribute, definindo-se a chave (name) e o valor
(value) do atributo. Esta abordagem foi usada no componente de identificador passthrough;
2. Por meio do marcador f:passThroughAttributes, definindo-se um mapa de valores (que será uma
implementação de java.util.Map<String, Object>) que será associado a um conjunto de atributos. Esta
abordagem foi usada no componente de identificador passthrough2;
3. Por meio do uso de atributos inseridos no próprio marcador que representa o elemento HTML, seguindo
um espaço de nomes específico para atributos pass-through (http://xmlns.jcp.org/jsf/passthrough) declarado
e prefixado (neste caso, com pt, de pass-through) na view em questão (<html>). Esta abordagem foi usada
no componente de identificador passthrough3. Perceba que, neste caso usamos tanto um atributo com
semântica previamente conhecida (placeholder, que define um texto de apoio/sugestão ao usuário,
popularmente conhecido como hint text) quanto outro absolutamente arbitrário (chaveQualquer), ao qual
não há semântica alguma previamente associada.
Listagem 1. Página home.xhtml, que trabalha com atributos pass-through aplicados a facelets.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:pt="http://xmlns.jcp.org/jsf/passthrough">
<h:head>
<title>Java Server Faces 2.2 - Java Magazine</title>
</h:head>
<h:body>
<div align="center" >
<h:form prependId="false">
<h:outputText value="Testando pass-through individual:" />
<h:inputText value="#{htmlFriendlyMB.passThroughAttribute}"
id="passthrough" binding=
"#{htmlFriendlyMB.campoTexto}" >
<f:passThroughAttribute name="passthroughAttr1"
value="Testando passthrough" />
</h:inputText> <br/><br/>
<h:outputText value="Testando pass-through via mapa:" />
<h:inputText value="#{htmlFriendlyMB.passThroughAttribute}"
id="passthrough2" binding=
"#{htmlFriendlyMB.campoTexto2}" >
<f:passThroughAttributes
value="#{htmlFriendlyMB.passThroughAttributeMap}" />
</h:inputText> <br/><br/>
<h:outputText value="Testando pass-through via namespace:" />
<h:inputText value="#{htmlFriendlyMB.passThroughAttribute}"
pt:placeholder="Entre com o valor"
id="passthrough3" pt:chaveQualquer="Valor arbitrário"
binding="#{htmlFriendlyMB.campoTexto3}"/> <br/><br/>
<h:commandButton id="cmdPassThrough" value="Analisar"
action="#{htmlFriendlyMB.processarAtributosPassThrough()}" />
</h:form>
</div>
</h:body>
</html>
A recuperação dos valores pass-through associados a elementos HTML, no lado servidor, é possível a partir
de uma nova API incorporada à classe javax.faces.component.UIComponent, assim definida:
1. getPassThroughAttributes(boolean criarMapa): java.util.Map<String, Object> – este método tem
como parâmetro uma variável booleana que, ao ser valorada com true, estabelece que um novo mapa de
objetos deva ser criado caso ainda não exista um mapa de atributos pass-through vinculado ao componente.
Quando este parâmetro for false, no entanto, estabelece-se que null será retornado caso este componente não
tenha um mapa de atributos pass-through associado a ele no instante em que o método for invocado;
2. getPassThroughAttributes(): java.util.Map<String, Object> – este método é uma simplificação do
anterior. Na prática, a sua execução consiste em uma chamada a getPassThroughAttributes(boolean)
passando true como valor de entrada. Usando-se este método, portanto, um ponteiro nulo jamais será
devolvido a quem o invocou;
Observe a Listagem 2, na qual temos um bean gerenciado fazendo uso dos métodos recém-listados. Cada
componente usado na construção da árvore que representa a view da Listagem 1 possui um mapa próprio de
atributos pass-through, e cada um dos valores pode ser facilmente recuperado através da chave que o
identifica nesta coleção. Para facilitar a demonstração prática disto, fizemos uso do atributo binding nos
elementos da Listagem 1 para associá-los a instâncias de componentes definidas programaticamente no
bean gerenciado da Listagem 2, a partir dos atributos nomeados como campoTexto, campoTexto2 e
campoTexto3.
Além dos campos de texto citados acima, a view da Listagem 1 também contém um botão, identificado
como cmdPassThrough. Quando ele é clicado, dispara uma chamada ao método de assinatura
processarAtributosPassThrough(), implementado pela classe HtmlFriendlyMB. Este método, uma vez
chamado, dispara uma chamada a três outros métodos privados, em sequência, assim definidos:
1. analisarAtributoPassThrough():void – percorre o mapa de atributos do componente identificado pelo
ID passthrough e imprime o valor de cada atributo na console;
2. percorrerMapaDeAtributos():void – percorre o mapa de atributos do componente identificado pelo ID
passthrough2 e imprime seus valores na console;
3. analisarPassthroughViaNamespace():void – percorre o mapa de atributos do componente identificado
pelo ID passthrough3 e imprime o valor de cada atributo na console.
Listagem 2. HtmlFriendlyMB.java: bean gerenciado que trata do processamento de atributos pass-through.
package br.com.devmedia.jsf22.controller;
import java.util.HashMap;
import java.util.Map;
import javax.inject.Named;
import javax.enterprise.context.RequestScoped;
import javax.faces.component.UIComponent;
/**
* Bean gerenciado usado para mostrar os recursos
da característica de
* marcadores amigáveis ao HTML 5 do JSF 2.2.
*
* @author pedrobrigatto
*/
@Named(value = "htmlFriendlyMB")
@RequestScoped
public class HtmlFriendlyMB {
private String passThroughAttribute;
private Map<String, Object> passThroughAttributeMap;
private UIComponent campoTexto;
private UIComponent campoTexto2;
private UIComponent campoTexto3;
private int contador = 0;
public HtmlFriendlyMB() {
this.passThroughAttributeMap = new HashMap<>();
this.passThroughAttributeMap.put("Atributo1", "Testando");
this.passThroughAttributeMap.put("Atributo2", "Atributos");
this.passThroughAttributeMap.put("Atributo3", "Pass-through");
}
//... getters e setters para todos os atributos do bean gerenciado
public void processar() {
resultado = String.format("Este texto já foi
modificado %d vezes", ++contador);
System.out.println(resultado);
}
/**
* Método usado para processar atributos pass-through,
recurso disponibilizado
* na versão 2.2. da especificação JSF.
*/
public void processarAtributosPassThrough() {
analisarAtributoPassThrough();
percorrerMapaDeAtributos();
analisarPassthroughViaNamespace();
}
private void analisarAtributoPassThrough() {
if (campoTexto != null) {
System.out.println(String.format(
"@@@@@ O atributo foi recuperado e tem valor %s",
campoTexto.getPassThroughAttributes()
.get("passthroughAttr1").
toString()));
}
}
private void percorrerMapaDeAtributos() {
if (campoTexto2 != null) {
System.out.println(String.format(
"##### O ID do campo utilizando mapa de
atributos pass-through é %s",
campoTexto.getId()));
Map<String, Object> mapa = campoTexto2
.getPassThroughAttributes();
for (Map.Entry item : mapa.entrySet()) {
System.out.println(String.format(
">>>>> Atributo pass-through
detectado com chave
%s e valor %s", item.getKey().
toString(), item.getValue().toString()));
}
}
}
private void analisarPassthroughViaNamespace() {
if (campoTexto3 != null) {
Map<String, Object> mapa = campoTexto3.
getPassThroughAttributes();
for (Map.Entry item : mapa.entrySet()) {
System.out.println(String.format(
">>>>> Atributo pass-through detectado
com chave %s e valor %s",
item.getKey().toString(), item.getValue().toString()));
}
}
}
public Map<String, Object> getPassThroughAttributeMap() {
return passThroughAttributeMap;
}
public void setPassThroughAttributeMap
(Map<String, Object>
passThroughAttributeMap) {
this.passThroughAttributeMap =
passThroughAttributeMap;
}
}
Por fim, na Listagem 3, apresentamos o texto impresso como resultado da execução do código acima
descrito. Ali, podemos ver que, de fato, todos os atributos associados aos Facelets usados na construção da
view da Listagem 1 foram recuperados e analisados com sucesso.
Atributos pass-through, como vimos, podem ser arbitrários, isto é, identificados livremente de acordo com
os planos da equipe de desenvolvimento de projetos em Java para a web. Dessa forma, podemos rechear
componentes de uma view com um conjunto variável de informações adicionais e que podem influenciar, no
ambiente servidor, tanto no modelo de dados quanto no próprio visual da página atual ou de uma página
seguinte do fluxo em questão. Caberá, portanto, ao programador decidir se o conjunto de atributos da
própria API de componentes JSF é suficiente para guardar todas as informações de uma view ou se o uso de
um conjunto adicional de dados, identificados arbitrariamente, pode ser interessante. Em qualquer um dos
casos, o fato importante a ser registrado aqui é que, agora, isto é não apenas possível, mas extremamente
simples de se fazer em JSF.
Vejamos agora como funciona o conceito de elementos pass-through.
Listagem 3. Log do servidor contendo os valores dos atributos pass-through recuperados em tempo de
execução.
INFO: JM-JSF22-1 foi implantado com sucesso em 6,738 milissegundos.
INFO: @@@@@ O atributo foi recuperado e tem valor Testando passthrough
INFO: ##### O ID do campo utilizando mapa de atributos pass-through ?
passthrough
INFO: >>>>> Atributo pass-through detectado com
chave Atributo2 e valor Atributos
INFO: >>>>> Atributo pass-through detectado com
chave Atributo1 e valor Testando
INFO: >>>>> Atributo pass-through detectado com
chave Atributo3 e valor Pass-through
INFO: >>>>> Atributo pass-through detectado com
chave chaveQualquer e valor Valor arbitrário
INFO: >>>>> Atributo pass-through detectado com
chave placeholder e valor Entre com o valor
Elementos pass-through
Esta abordagem traz ainda mais liberdade ao autor de páginas em sistemas web desenvolvidos sobre a
plataforma Java EE. Enquanto, com atributos pass-through, falamos exclusivamente de campos associados a
componentes de uma view (escritos a partir da VDL Facelets), agora trataremos de elementos de uma view
declarados a partir de HTML puro e que, por serem assim declarados, passam sobre o mecanismo
convencional de renderização do JSF.
A grande marca desta estratégia está no fato de que, muito embora o conteúdo de uma view possa ser
construído a partir de elementos escritos em HTML, não há prejuízo algum para o sistema em si no tocante a
todos os recursos que o JSF provê no ambiente servidor. A “mágica”, neste caso, fica por conta de uma peça
central de “decoração” de conteúdo, responsável pelo mapeamento de elementos HTML para elementos
JSF. A API responsável por este processo está contida na classe javax.faces.view.facelets.TagDecorator,
sensivelmente aprimorada nesta nova versão da especificação. Este “decorador” é colocado para trabalhar
em uma fase do ciclo de vida conhecida como Restauração da view, abordada há pouco. Nesta fase, a
instância de javax.faces.view.ViewDeclarationLanguage que representa a view atual passa por sua criação
ou restauração. O método no qual este processo de restauração ocorre pertence à API de
ViewDeclarationLanguage e tem a seguinte assinatura:
buildView(FacesContext, UIViewRoot)
Este método é invocado a partir do já descrito controlador frontal da especificação JSF, FacesServlet, nos
momentos iniciais do tratamento da requisição de usuário.
O fluxo completo é denso, mas pode ser resumido pelo diagrama da Figura 2. Por ela, vemos que o
decorador atua como um agente do framework que, analisando cada marcador definido na view em questão,
buscará em uma tabela o seu elemento JSF correspondente. Caso não o encontre, atribuirá a ele um
mapeamento genérico, como veremos mais adiante no texto. Desta forma, ainda que o programador/designer
crie uma view fazendo uso apenas de HTML, serviços do framework farão com que ela tenha sua
representação no lado servidor, tal como acontece com views desenhadas a partir de Facelets.
Figura 2. Representação do processo de decoração de marcadores.
Mas, afinal de contas, de onde vêm os dados para que o mapeamento citado ocorra? Quais são os critérios e
as informações que o decorador utilizará para que este processo se conclua com sucesso? A resposta está na
Tabela 2. Este é o conjunto de dados no qual o decorador se baseia para, marcador a marcador definido na
view, encontrar o seu elemento JSF correspondente. Perceba que, para uma grande gama de marcadores
HTML, há um conjunto de combinações possíveis que culminam na definição do marcador alvo na
biblioteca JSF. Para os casos em que não há uma regra de mapeamento específica, a documentação
estabelece que ele seja associado ao elemento genérico “elemento”. Uma vez que o conteúdo a ser
renderizado é HTML puro, não existe a necessidade de se mapear todos os elementos HTML a componentes
específicos da biblioteca do JSF; entretanto, algumas associações são essenciais, como, por exemplo, o
bloco lógico a ser executado no clique de um botão de uma página. A grande vantagem do uso de elementos
pass-through é a possibilidade de, a partir deles, desenharmos páginas web somente com a linguagem
HTML, ignorando o processo de renderização do JSF sem perder, com isso, todos os demais recursos do
framework no tratamento de views do lado servidor. Para entendermos melhor como tudo isso funciona,
vejamos o conteúdo da Listagem 4.
Listagem 4. Index.xhtml – página que faz uso de elementos pass-through.
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:jsf="http://xmlns.jcp.org/jsf"
xmlns:f="http://java.sun.com/jsf/core">
<head jsf:id="head">
<title>Putting it all together </title>
</head>
<body jsf:id="body">
<form jsf:id="form" jsf:prependId="false">
<label jsf:for="name">Name </label>
<input jsf:id="name" type="text"
jsf:value="#{htmlFriendlyMB2.name}" />
<label jsf:for="tel">Tel </label>
<input jsf:id="tel" type="text"
jsf:value="#{htmlFriendlyMB2.tel}" />
<label jsf:for="email">Email </label>
<input jsf:id="email" type="text"
jsf:value="#{htmlFriendlyMB2.email}" />
<input type="submit" value="Executar" jsf:id="acao"
jsf:action="#{htmlFriendlyMB2.processar}" >
<f:ajax execute="@form" render="name tel email"
event="action" />
</input>
</form>
</body>
</html>
Tabela 2. Base para decoração de elementos HTML.
Nela, todos os elementos da view foram escritos em HTML 5. Não há, como na Listagem 1, o uso de
Facelets para a composição da estrutura da página. Ou seja: há, aqui, uma clara independência em relação
aos elementos JSF para a definição da interface com o usuário, deixando o programador ou designer web
absolutamente livre para trabalhar todos os seus conceitos de experiência de usuário de forma bem mais
ampla. Além disso, toda página desenvolvida apenas com elementos HTML pode ser facilmente aberta e
visualizada em qualquer navegador web sem a necessidade de submetê-la ao runtime JSF. No entanto,
devido ao já mencionado processo de decoração encapsulado pela classe TagDecorator, nenhum dos
recursos JSF é perdido pelo desenvolvedor no ambiente servidor. O mapeamento apresentado pela Tabela 2,
bem como todas as regras estabelecidas para a associação entre elementos HTML, view handlers, tag
handlers e renderizadores, garante que a árvore de componentes seja criada, mantida e represente, deste
modo, os estados de cada elemento da view em questão. Logo, o que temos no ambiente servidor ainda são
objetos JSF, o que nos permite trabalhar novamente com o conceito de valores pass-through através da
mesma API já citada.
Perceba que a view da Listagem 4 começa declarando o espaço de nomes “http://xmlns.jcp.org/jsf”,
identificado pelo prefixo jsf, no qual estão definidos atributos a serem usados ao longo de sua estrutura.
Todas as caixas de entrada de texto utilizadas trabalham com um dos atributos definidos neste espaço de
nomes, (jsf:id), para assim serem identificadas unicamente no documento. Além disso, trabalham também
com o atributo (jsf:value) para vincular os dados nelas preenchidas a atributos definidos em um bean
gerenciado, que veremos em instantes. O último elemento definido dentro do formulário é um botão de
submissão, e aqui mais um atributo deste espaço de nomes é utilizado (jsf:action) para vincular eventos de
ação disparados por ele a um método do mesmo bean gerenciado que descreveremos logo mais.
Até aqui, vimos como a view HTML foi montada. Conforme dito acima, todos os campos dela foram
vinculados a atributos e métodos de um bean gerenciado. Este bean gerenciado, apresentado na Listagem 5,
está implementado em uma classe de nome HtmlFriendlyMB2, configurada para estar acessível via EL
através do identificador htmlFriendlyMB2 e cujo escopo será de sessão. Os campos da página têm seus
valores vinculados, cada qual, a um atributo do tipo java.lang.String deste bean, e o botão de submissão de
formulário está configurado para disparar (na fase de Invocação da Aplicação do ciclo de vida, como já
vimos) um método cuja assinatura é a seguinte:
processar (): void
e que, no exemplo, apenas imprime o número de vezes em que o formulário foi submetido; valor este que
fica guardado em um contador que é incrementado a cada clique do botão. A Listagem 6 apresenta o log do
servidor quando este sistema é colocado novamente para executar. Como podemos notar, uma página cujos
elementos de interface estão todos escritos em HTML 5, fazendo uso de atributos personalizados e descritos
em um novo espaço de nomes da especificação JSF, consegue se comunicar com um bean gerenciado Java,
em um ambiente JSF, da mesma forma que já estamos acostumados a ver quando usamos Facelets ou JSF
como VDL para a construção das views de nossos sistemas.
Listagem 5. HtmlFriendlyMB2.java: bean gerenciado que exemplifica o uso de elementos pass-through
package br.com.devmedia.jsf22.controller;
import java.io.Serializable;
import javax.enterprise.context.SessionScoped;
import javax.inject.Named;
/**
* Bean gerenciado de escopo de sessão para exemplificar o
uso de elementos pass-through
* da especificação JSF 2.2.
*
* @author pedrobrigatto
*/
@Named(value = "htmlFriendlyMB2")
@SessionScoped
public class HtmlFriendlyMB2 implements Serializable {
private String name;
private String tel;
private String email;
private String resultado;
private int contador = 0;
public HtmlFriendlyMB2() {}
//... getters e setters para todos os atributos da classe
public void processar() {
resultado = String.format("Este texto já foi modificado
%d vezes", ++contador);
System.out.println(resultado);
}
}
Listagem 6. Log da aplicação exemplificando o uso de elementos pass-through
INFO: Inicializando Mojarra 2.2.0 ( 20130502-2118
https://svn.java.net/svn/mojarra~svn/tags/2.2.0@11930)
para o contexto '/JM-JSF22-1'
INFO: Monitoring jndi:/server/JM-JSF22-1/WEB-INF/
faces-config.xml for modifications
INFO: Loading application [JM-JSF22-1] at [/JM-JSF22-1]
INFO: JM-JSF22-1 foi implantado com sucesso em 3,004 milissegundos.
INFO: Este texto j? foi modificado 1 vezes
INFO: Este texto j? foi modificado 2 vezes
INFO: Este texto j? foi modificado 3 vezes
Isto é tudo o que precisamos saber, de início, a respeito do recurso de marcadores amigáveis à HTML 5.
Com tudo o que foi apresentado até aqui, o leitor já será capaz de fazer algumas experiências bem
interessantes e testar, por si mesmo, todos os benefícios trazidos a partir desta Big Ticket Feature. Vejamos,
a partir de agora, outra característica central do JSF 2.2: Faces Flow.
Faces Flow
Outra novidade trazida pelo JSF 2.2 é o suporte ao desenvolvimento de sistemas web orientados a fluxos
(workflows). Esta característica, intitulada Faces Flow, foi elaborada em cima dos melhores recursos
existentes em outros frameworks também orientados a fluxos e já consolidados no mercado. São eles:
· Apache MyFaces CODI;
· Oracle ADF Task Flow;
· Spring Web Flow.
A partir de agora, será possível encapsular um conjunto de views/páginas em um pacote independente de
todos os demais, totalmente reutilizável e que, sozinho, será capaz de representar uma funcionalidade do
sistema. Desta maneira, as aplicações deixam de ter um único e grande fluxo, e essas pequenas unidades
passam a ser combinadas para compor uma solução final.
Até a plataforma Java EE 6, o escopo que mais chegava próximo ao de um fluxo era o de conversação
(javax.enterprise.context.ConversationScoped), introduzido com a especificação CDI (Contexts and
Dependency Injection) em sua versão 1.0. No entanto, este escopo é um pouco mais amplo que o de um
workflow, pois uma “conversa” entre cliente e servidor pode ser composta por um conjunto variável de
fluxos. Para simplificar a visualização, uma conversação pode ser interpretada como uma aba de um
navegador web executando uma aplicação qualquer. Quando, por uma necessidade qualquer, o mesmo
usuário abre, em duas abas distintas, a mesma aplicação (ainda que em funções diferentes), terá à sua
disposição duas conversações independentes entre si, mas que compartilham da mesma sessão de usuário.
Fluxos, no entanto, éalgo diferente. São definidos por um ponto único de entrada, outro – também único – de
saída e um conjunto variável de passos intermediários. Também podem envolver parâmetros de entrada e de
saída, usados para receber valores de alguns fluxos e enviar valores para outros. Pontos de entrada, pontos
de saída e todos os outros elementos de um fluxo são conhecidos, na literatura, como nós, e é esta a
nomenclatura que usaremos no texto que se segue.
Para facilitar o entendimento, imagine um cadastro em sites de emprego, em que três etapas devem ser
cumpridas para que uma candidatura seja efetuada. Este processo está ilustrado na Figura 3.
Em um primeiro momento, preenchem-se os dados gerais do candidato (como nome, e-mail e telefones de
contato). Feito isso, a próxima etapa é apresentada, em que informações sobre escolaridade devem ser
fornecidas. A terceira e última etapa consiste na digitação de dados referentes ao histórico profissional da
pessoa. Somente quando todos esses dados são passados para o sistema é que o cadastramento desta pessoa
poderá, finalmente, ser realizado. Este é um caso clássico de fluxo, e o leque de cenários similares a ele
ilustra muito bem o quanto é importante, para uma especificação como a JavaServer Faces, ter em seu
arsenal um recurso “nativo” para a implementação de casos desta natureza.
Figura 3. Um modelo de tela para ilustrar o conceito de workflow.
Outro cenário tão ou mais comum quanto o exposto acima é o de venda de produtos pela Internet, em que
passos como Identificação do usuário, Preenchimento de dados de pagamento (cartão de crédito, data de
validade, parcelamento, bandeira, dentre outros) e Finalização da compra são, todos eles, necessários para
que, enfim, seja realizado o processamento da transação. Este é outro cenário que ajuda a esclarecer ao leitor
o conceito de um workflow de forma bem prática.
Este último caso mencionado nos ajuda muito a entender, inclusive, a questão do reuso de fluxos. Imagine
que, em sua empresa, seja necessário desenvolver um módulo de pagamento online para um site de comércio
eletrônico de um de seus clientes. Não é difícil imaginar que ele poderá ser útil, também, em projetos
similares, com escopos semelhantes. Por isso, seria extremamente interessante desenvolvê-lo como um
módulo, independente de um sistema específico, para poder “ligá-lo” em qualquer solução em que fosse
interessante. Reuso, portanto, é indubitavelmente o maior valor agregado por esta característica para o JSF.
Como citado anteriormente, Faces Flow muda o foco de páginas para nós. De acordo com a especificação,
são cinco os tipos possíveis de nós:
1. View: um pedaço de uma página ou uma página completa;
2. Chamada a método: invocação de métodos via Expression Language;
3. Switch: decisão de navegação baseada em condições booleanas;
4. Chamada a fluxo: consiste em uma chamada de um fluxo a outro;
5. Retorno de fluxo: consiste em uma configuração de saída de um fluxo.
Faces Flow demandou, ainda, a criação de duas novas anotações (CDI), sendo elas:
· @javax.faces.flow.FlowScoped: que associa o tempo de vida de beans gerenciados assim anotados ao
tempo de duração de um dado fluxo;
· @javax.faces.flow.builder.FlowDefinition: usada para configurar métodos de uma classe que têm por
objetivo construir, programaticamente, um fluxo dentro de uma aplicação.
A especificação também define um novo objeto acessível via EL, identificado pelo nome flowScope. Para
acessá-lo dentro de uma view, por exemplo, basta que utilizemos a notação #{flowScope}, exatamente como
já fazemos com beans gerenciados e outros objetos JSF pré-configurados, como application. Quando este
objeto de escopo é acessado via EL em views, o que ocorre nos bastidores é uma chamada ao método
facesContext.getApplication().getFlowHandler().getCurrentFlowScope(). É dentro deste objeto de
escopo que, por exemplo, serão guardados parâmetros que devem ser passados de um fluxo para outro,
conforme veremos daqui a instantes.
Há uma série de outros detalhes aos quais devemos nos atentar. Para não tornar a análise maçante, no
entanto, faremos uso de um exemplo contido no tutorial da especificação Java EE, que tem como tema a
realização de checkouts em uma loja online, hipotética. Este exemplo pode ser baixado através de uma
ferramenta do GlassFish chamada Update Tool, e as instruções de como fazê-lo estão na seção Links no
final do artigo.
Começaremos pela ilustração da Figura 4, que apresenta a comunicação entre os fluxos propostos no
projeto mencionado.
Figura 4. Representação dos fluxos da aplicação de checkout.
Pelo diagrama, vemos dois fluxos implementados. O primeiro deles, chamado joinFlow, foi configurado
declarativamente (ou seja, através de um arquivo XML, chamado joinFlow-flow.xml) e contém apenas dois
nós, identificados por joinFlow.xhtml e joinFlow2.xhtml. O segundo, chamado checkoutFlow, foi
configurado através da API de Faces Flow e é constituído de quatro nós, identificados pelos nomes
checkoutFlow.xhtml, checkoutFlow2.xhtml, checkoutFlow3.xhtml e checkoutFlow4.xhtml. Ambos os fluxos
utilizam beans gerenciados para guardar valores de entrada e processar ações das views neles contidas.
Passaremos, agora, à análise do primeiro fluxo citado, joinFlow. Observe a Listagem 7, na qual é
apresentado o arquivo XML usado para configurá-lo. A definição do fluxo, representada pelo marcador
<flow-definition>, tem um atributo denominado id usado para estabelecer o seu identificador único em toda
a aplicação. Logo em seguida, um nó de retorno é configurado, de modo que seu valor seja recuperado,
dinamicamente, através de um atributo de um bean gerenciado, o qual veremos em detalhes mais adiante.
Parâmetros de entrada também são definidos (param1CheckoutFlow e param2CheckoutFlow), os quais
serão usados para a transmissão de valores iniciais de checkoutFlow para joinFlow. Por fim, um nó de
chamada de fluxo é configurado para estabelecer o modo pelo qual joinFlow invocará checkoutFlow, que é
exatamente a comunicação entre fluxos ilustrada na Figura 4. Perceba que, para esta chamada, dois
parâmetros de saída são definidos (param1FromJoinFlow e param2FromJoinFlow), sobre os quais
voltaremos a falar logo mais.
A convenção de Faces Flow determina que o nó de entrada de todo fluxo é aquele que recebe exatamente o
mesmo nome do fluxo propriamente dito. Este é o caso de joinFlow.xhtml, apresentado na Listagem 8. Este
é um código particularmente rico, através do qual conseguiremos visualizar boa parte dos elementos citados
até aqui.
Listagem 7. joinFlow-flow.xml. Configuração do fluxo joinFlow de forma declarativa (XML).
<?xml version='1.0' encoding='UTF-8'?>
<!--
Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved.
You may not modify, use, reproduce, or distribute this software except in
compliance with the terms of the License at:
http://java.net/projects/javaeetutorial/pages/BerkeleyLicense
-->
<faces-config version="2.2" xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_2.xsd">
<flow-definition id="joinFlow">
<flow-return id="returnFromJoinFlow">
<from-outcome>#{joinFlowBean.returnValue}</from-outcome>
</flow-return>
<inbound-parameter>
<name>param1FromCheckoutFlow</name>
<value>#{flowScope.param1Value}</value>
</inbound-parameter>
<inbound-parameter>
<name>param2FromCheckoutFlow</name>
<value>#{flowScope.param2Value}</value>
</inbound-parameter>
<flow-call id="callcheckoutFlow">
<flow-reference>
<flow-id>checkoutFlow</flow-id>
</flow-reference>
<outbound-parameter>
<name>param1FromJoinFlow</name>
<value>param1 joinFlow value</value>
</outbound-parameter>
<outbound-parameter>
<name>param2FromJoinFlow</name>
<value>param2 joinFlow value</value>
</outbound-parameter>
</flow-call>
</flow-definition>
</faces-config>
Listagem 8. joinFlow.xhtml. Ponto de entrada do fluxo identificado como joinFlow.
<?xml version='1.0' encoding='UTF-8' ?>
<!--
Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved.
You may not modify, use, reproduce, or distribute this software except in
compliance with the terms of the License at:
http://java.net/projects/javaeetutorial/pages/BerkeleyLicense
-->
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html lang="en"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core">
<h:head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<h:outputStylesheet library="css" name="stylesheet.css"/>
<title>Join Flow Page One</title>
</h:head>
<h:body style="background-color: lightgoldenrodyellow">
<h:form prependId="false">
<h:graphicImage url="#{resource['images:duke.books.gif']}"
alt="Duke carrying books"/>
<h2>Join Flow Page One</h2>
<p>Thank you,
<span id="param1FromCheckoutFlow">
#{flowScope.param1Value}</span>
from
<span id="param2FromCheckoutFlow">
#{flowScope.param2Value}</span>.
(Reading the parameters passed in from the checkout flow,
if there are any.)</p>
<p>Check the boxes to receive our newsletters
or special offers.</p>
<h:panelGrid columns="2" role="presentation">
<h:outputLabel for="clubcheck"
value="Yes! I'd like to join the Duke Fan Club!"/>
<h:selectBooleanCheckbox id="clubcheck"
value="#{joinFlowBean.fanClub}"/>
<h:outputLabel for="newslettercheckbox"
value="Send me these FREE newsletters:"/>
<h:selectManyCheckbox id="newslettercheckbox"
layout="pageDirection"
value="#{joinFlowBean.newsletters}">
<f:selectItems value="#{joinFlowBean.newsletterItems}"/>
</h:selectManyCheckbox>
</h:panelGrid>
<p>Click Submit to continue, or Home to go to the
site home page.</p>
<p><h:commandButton value="Submit"
action="joinFlow2"/></p>
<p><h:commandButton value="Exit Flow"
action="returnFromJoinFlow"/></p>
</h:form>
</h:body>
</html>
O primeiro desses elementos e sobre o qual falaremos é o parâmetro de entrada, que pode ser usado para
passar valores de um fluxo a outro. Todo parâmetro, como já mencionamos, é guardado no escopo de fluxo,
e de lá recuperado quando for preciso. Na view de entrada de joinFlow, apresentado na Listagem 7, há dois
desses campos posicionados logo no início do código. Perceba que, identificados pelos nomes
param1FromCheckoutFlow e param2FromCheckoutFlow, eles usam exatamente o objeto de escopo
para recuperar seus valores, que estarão guardados, respectivamente, nos atributos param1Value e
param2Value de flowScope.
Outro ponto também destacado no código desta view é a configuração de um nó de retorno, mapeado em um
de seus botões de comando. Perceba que o identificador do valor de retorno é exatamente aquele usado na
Listagem 7, sendo suficiente para que a navegação seja realizada corretamente. Além deste botão, temos
outro que apenas aponta para o próximo passo de joinFlow.
O bean gerenciado responsável pelo tratamento de joinFlow pode ser analisado na Listagem 9. Não
tomaremos muito tempo com este código, pois a única novidade em relação ao que já conhecemos sobre JSF
é o uso do novo escopo do CDI, FlowScoped, que garante que o tempo de vida de objetos desta classe seja
exatamente o tempo de vida do fluxo ao qual eles sejam associados. Ao utilizarmos este tipo de escopo em
beans gerenciados, devemos apenas nos preocupar em garantir que o ID do fluxo associado ao escopo seja
exatamente aquele do fluxo ao qual desejamos vincular esta classe.
Fechando a análise deste primeiro fluxo, temos a Listagem 10, com o conteúdo de seu segundo e definitivo
passo. O ponto que merece destaque, aqui, é a referência ao segundo fluxo da aplicação, checkoutFlow. Isto
é feito configurando-se um dos botões da view para disparar a execução do nó de chamada de fluxo
configurado em joinFlow, o qual, por sua vez, já se encontra configurado para encontrar e iniciar o fluxo de
checkout, como já tivemos a oportunidade de ver na Listagem 7.
Listagem 9. JoinFlowBean.java. Bean gerenciado que tratará especificamente da execução do fluxo
joinFlow.
/**
* Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved.
*
* You may not modify, use, reproduce, or distribute this software except in
* compliance with the terms of the License at:
* http://java.net/projects/javaeetutorial/pages/BerkeleyLicense
*/
package javaeetutorial.checkoutmodule;
import java.io.Serializable;
import javax.inject.Named;
import javax.faces.flow.FlowScoped;
import javax.faces.model.SelectItem;
/**
* Backing bean for JoinFlow.
*/
@Named
@FlowScoped("joinFlow")
public class JoinFlowBean implements Serializable {
private static final long serialVersionUID = 1L;
private boolean fanClub;
private String[] newsletters;
private static final SelectItem[] newsletterItems = {
new SelectItem("Duke's Quarterly"),
new SelectItem("Innovator's Almanac"),
new SelectItem("Duke's Diet and Exercise Journal"),
new SelectItem("Random Ramblings")
};
public JoinFlowBean() {
this.newsletters = new String[0];
}
public String getReturnValue() {
return "/exithome";
}
public boolean isFanClub() {
return fanClub;
}
public void setFanClub(boolean fanClub) {
this.fanClub = fanClub;
}
public String[] getNewsletters() {
return newsletters;
}
public void setNewsletters(String[] newsletters) {
this.newsletters = newsletters;
}
public SelectItem[] getNewsletterItems() {
return newsletterItems;
}
}
Listagem 10. joinFlow2.xhtml. Segunda e última página do fluxo de nome “joinFlow”.
<?xml version='1.0' encoding='UTF-8' ?>
<!--
Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved.
You may not modify, use, reproduce, or distribute this software except in
compliance with the terms of the License at:
http://java.net/projects/javaeetutorial/pages/BerkeleyLicense
-->
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html lang="en"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html">
<h:head>
<meta http-equiv="Content-Type" content="text/html;
charset=UTF-8" />
<h:outputStylesheet library="css" name="stylesheet.css"/>
<title>Join Flow Page Two</title>
</h:head>
<h:body style="background-color: lightgoldenrodyellow">
<h:form prependId="false">
<h:graphicImage url="#{resource['images:duke.books.gif']}"
alt="Duke carrying books"/>
<h2>Join Flow Page Two</h2>
<p>Thank you, #{flowScope.param1Value}!
Reading the parameter passed from
the Checkout flow.</p>
<p>To call the checkout flow,
click Check Out.</p>
<p>To go to the site's home page or to return
to the calling flow,
click Home.</p>
<p><h:commandButton value="Check Out"
action="callcheckoutFlow"/></p>
<p><h:commandButton value="Exit Flow"
action="returnFromJoinFlow"/></p>
</h:form>
</h:body>
</html>
Vamos, agora, dedicar algumas palavras ao processo de checkout (checkoutFlow). A mudança, neste caso,
está na configuração do fluxo, que, diferentemente do modelo declarativo de joinFlow, será definido
programaticamente a partir da API de Faces Flow. Na Listagem 11, apresentamos a página de entrada deste
fluxo que, da mesma forma que em joinFlow, é tida como nó de entrada do fluxo por levar o seu nome.
Além disso, assim como também foi feito no fluxo apresentado anteriormente, há parâmetros de entrada
sendo recebidos na view a partir do objeto de fluxo de escopo mantido pelo framework.
A Listagem 12 contém uma classe na qual o fluxo é, então, configurado. A API central de Faces Flow está
definida em javax.faces.flow.builder.FlowBuilder. A partir de uma instância desta classe, somos capazes
de fazer tudo aquilo que conseguimos através de um arquivo XML, como o visto na Listagem 7, em código
Java. Na verdade, quando optamos pela configuração declarativa, estamos delegando ao framework JSF a
construção do fluxo em questão, o que, por sua vez, acaba resultando em uma chamada a esta API, de forma
transparente.
Listagem 11. checkoutFlow.xhtml. Ponto de entrada do fluxo de checkout.
<?xml version='1.0' encoding='UTF-8' ?>
<!--
Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved.
You may not modify, use, reproduce, or distribute this software except in
compliance with the terms of the License at:
http://java.net/projects/javaeetutorial/pages/BerkeleyLicense
-->
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html lang="en"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core">
<h:head>
<meta http-equiv="Content-Type" content="text/html;
charset=UTF-8" />
<h:outputStylesheet library="css" name="stylesheet.css"/>
<title>Checkout Flow Page One</title>
</h:head>
<h:body style="background-color: lightblue">
<h:form prependId="false">
<h:graphicImage url="#{resource['images:duke.books.gif']}"
alt="Duke carrying books"/>
<h2>Checkout Flow Page One</h2>
<p>Thank you for your order of #{checkoutBean.numItems}
books at a cost of
<h:outputText value="#{checkoutBean.cost}">
<f:convertNumber currencySymbol="$" type="currency"/>
</h:outputText>.</p>
<p>Please provide your shipping information. Required fields are
marked with an asterisk (*).</p>
<h3>Shipping Information</h3>
<h:panelGrid columns="3" role="presentation">
<h:outputLabel value="Name: * " for="name"/>
<h:inputText id="name" value="#{checkoutFlowBean.name}"
required="true"/>
<h:message for="name" style="color: #D20005"/>
<h:outputLabel value="Address Line 1: * " for="addr1"/>
<h:inputText id="addr1"
value="#{checkoutFlowBean.addressLine1}"
required="true"/>
<h:message for="addr1" style="color: #D20005"/>
<h:outputLabel value="Address Line 2:" for="addr2"/>
<h:inputText id="addr2"
value="#{checkoutFlowBean.addressLine2}"/>
<h:message for="addr2" style="color: #D20005"/>
<h:outputLabel value="City: * " for="city"/>
<h:inputText id="city" value="#{checkoutFlowBean.city}"
required="true"/>
<h:message for="city" style="color: #D20005"/>
<h:outputLabel value="State:" for="state"/>
<h:inputText id="state" value="#{checkoutFlowBean.state}"
maxlength="2"/>
<h:message for="state" style="color: #D20005"/>
<h:outputLabel value="Zip/Postcode: * " for="zip"/>
<h:inputText id="zip" value="#{checkoutFlowBean.postalCode}"
required="true"/>
<h:message for="zip" style="color: #D20005"/>
<h:outputLabel value="Country: * " for="country"/>
<h:inputText id="country" value="#{checkoutFlowBean.country}"
required="true"/>
<h:message for="country" style="color: #D20005"/>
</h:panelGrid>
<p>If you called this flow from the Join flow,
you can see these parameters:
"<h:outputText value="#{flowScope.param1Value}"/>" and
"<h:outputText value="#{flowScope.param2Value}"/>"
</p>
<p>Click Continue to move to the next page
of the flow.</p>
<p>Click Cancel to exit the flow and return to
the starting page.</p>
<p></p>
<p><h:commandButton value="Continue"
action="checkoutFlow2"/></p>
<p><h:commandButton value="Exit Flow"
action="returnFromCheckoutFlow"/></p>
</h:form>
</h:body>
</html>
Listagem 12. CheckoutFLow.java. Classe usada para se configurar o fluxo de checkout programaticamente.
/**
* Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved.
*
* You may not modify, use, reproduce, or distribute this software except in
* compliance with the terms of the License at:
* http://java.net/projects/javaeetutorial/pages/BerkeleyLicense
*/
package javaeetutorial.checkoutmodule;
import java.io.Serializable;
import javax.enterprise.inject.Produces;
import javax.faces.flow.Flow;
import javax.faces.flow.builder.FlowBuilder;
import javax.faces.flow.builder.FlowBuilderParameter;
import javax.faces.flow.builder.FlowDefinition;
public class CheckoutFlow implements Serializable {
private static final long serialVersionUID = 1L;
@Produces
@FlowDefinition
public Flow defineFlow(@FlowBuilderParameter FlowBuilder flowBuilder) {
String flowId = "checkoutFlow";
flowBuilder.id("", flowId);
flowBuilder.viewNode(flowId, "/" + flowId + "/" + flowId + ".xhtml").
markAsStartNode();
flowBuilder.returnNode("returnFromCheckoutFlow").
fromOutcome("#{checkoutFlowBean.returnValue}");
flowBuilder.inboundParameter("param1FromJoinFlow",
"#{flowScope.param1Value}");
flowBuilder.inboundParameter("param2FromJoinFlow",
"#{flowScope.param2Value}");
flowBuilder.flowCallNode("calljoin").flowReference("", "joinFlow").
outboundParameter("param1FromCheckoutFlow",
"#{checkoutFlowBean.name}").
outboundParameter("param2FromCheckoutFlow",
"#{checkoutFlowBean.city}");
return flowBuilder.getFlow();
}
}
Toda vez que quisermos definir, em nossos sistemas, um método produtor de fluxos JSF, deveremos seguir
os passos listados a seguir:
· O método precisa ser configurado como produtor, através da anotação CDI
@javax.enterprise.inject.Produces;
· O método precisa ser configurado, também, com a anotação @javax.faces.flow.builder.FlowDefinition,
que garante que o que será produzido pelo método em questão é um fluxo (javax.faces.flow.Flow);
· O método deve retornar uma instância de javax.faces.flow.Flow;
· O método deve receber, por parâmetro, uma instância de javax.faces.flow.builder.FlowBuilder, que será
usado para configurar, de fato, o fluxo em questão;
Pelo código da Listagem 12, o leitor pode perceber que o que é feito ali é exatamente aquilo que já vimos
no arquivo XML apresentado na Listagem 7. Por este motivo, não investiremos tempo em analisar o
conteúdo deste método.
Na Listagem 13, apresentamos o nó de saída de checkoutFlow, que em muito se assemelha ao conteúdo da
Listagem 10. Vemos, ali, uma ação mapeada para saída de checkoutFlow e também uma ação de entrada
para joinFlow, que estabelece uma comunicação entre esses dois fluxos no sentido inverso ao que já vimos
quando analisamos joinFlow.
Listagem 13. checkoutFlow4.xhtml. Última página do fluxo de checkout.
<?xml version='1.0' encoding='UTF-8' ?>
<!--
Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved.
You may not modify, use, reproduce, or distribute this software except in
compliance with the terms of the License at:
http://java.net/projects/javaeetutorial/pages/BerkeleyLicense
-->
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html lang="en"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html">
<h:head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<h:outputStylesheet library="css" name="stylesheet.css"/>
<title>Checkout Flow Page Four</title>
</h:head>
<h:body style="background-color: lightblue">
<h:form prependId="false">
<h:graphicImage url="#{resource['images:duke.books.gif']}"
alt="Duke carrying books"/>
<h2>Checkout Flow Page Four</h2>
<p>Thank you for your order,
<h:outputText value="#{checkoutFlowBean.name}"/>!</p>
<p>Your order will be billed to
<h:outputText value="#{checkoutFlowBean.ccName}"/>.</p>
<p>To join our mailing list or club, click Join. This action will
call the Join flow.</p>
<p>To return to the index page or to the first page of the calling
flow, click Return.</p>
<p><h:commandButton value="Join" action="calljoin"/></p>
<p><h:commandButton value="Exit Flow"
action="returnFromCheckoutFlow"/></p>
</h:form>
</h:body>
</html>
As Listagens 14 e 15 foram apresentadas aqui apenas para fecharmos a composição dos ciclos. A Listagem
14 mostra a página de entrada da aplicação, onde cada um dos botões nela incluídos aponta para os fluxos
que vimos até o momento. A ação desses botões é estática e tem como valor o nome dos nós iniciais de cada
fluxo. Uma vez que essas páginas sejam acessadas, pela convenção que já foi apresentada ao leitor
anteriormente, é detectada a entrada em um fluxo, que passará a ser executado. A Listagem 15, por sua vez,
é uma página que representa o nó de saída do fluxo joinFlow, que contém um botão que redireciona o
usuário para a página inicial, já citada, contida na Listagem 14.
Listagem 14. Index.xhtml. Página inicial da aplicação.
<!--
Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved.
You may not modify, use, reproduce, or distribute this software except in
compliance with the terms of the License at:
http://java.net/projects/javaeetutorial/pages/BerkeleyLicense
-->
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html lang="en"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core">
<h:head>
<meta http-equiv="Content-Type" content="text/html;
charset=UTF-8" />
<h:outputStylesheet library="css" name="stylesheet.css"/>
<title>Checkout Module: Entry Page (Order)</title>
</h:head>
<h:body>
<h:form prependId="false">
<h:graphicImage url="#{resource['images:duke.books.gif']}"
alt="Duke carrying books"/>
<h2>Order Page</h2>
<p>You have ordered #{checkoutBean.numItems}
books at a cost of
<h:outputText value="#{checkoutBean.cost}">
<f:convertNumber currencySymbol="$" type="currency"/>
</h:outputText>.</p>
<p>Click Check Out to check out your order
and enter the checkout
flow.</p>
<p><h:commandButton value="Check Out"
action="checkoutFlow"/></p>
<p>Click Join to join our mailing list or club and enter the join
flow.</p>
<p><h:commandButton value="Join" action="joinFlow"/></p>
<p>This page is the exit point for the checkout flow pages.</p>
</h:form>
</h:body>
</html>
Listagem 15. exitHome.xhtml. Página de saída do fluxo intitulado joinFlow.
<?xml version='1.0' encoding='UTF-8' ?>
<!--
Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved.
You may not modify, use, reproduce, or distribute this software except in
compliance with the terms of the License at:
http://java.net/projects/javaeetutorial/pages/BerkeleyLicense
-->
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html">
<h:head>
<meta http-equiv="Content-Type" content="text/html;
charset=UTF-8" />
<h:outputStylesheet library="css"
name="stylesheet.css"/>
<title>Checkout Module: Exit Page (Home)</title>
</h:head>
<h:body>
<h:form>
<h:graphicImage url="#{resource['images:duke.books.gif']}"
alt="Duke carrying books"/>
<h2>Home/Exit Page</h2>
<p>Imagine that this is the home page of the company.
When you exit the join flow pages,
you land here.</p>
<p><h:commandButton value="Go to Checkout Page"
action="index"/></p>
</h:form>
</h:body>
</html>
Conclusão
Chegamos ao final do primeiro artigo da série sobre as novidades do JSF 2.2. Ao longo do texto, vimos duas
das mais significativas características incorporadas à especificação, que abrem agora a chance de se
trabalhar nativamente com fluxos e permitem, também, que se explore de forma livre e plena os recursos do
HTML 5 no desenvolvimento da interface gráfica de aplicações JSF. Essas são duas das quatro
características mais impactantes desta revisão do framework, restando ainda outras duas a serem abordadas
no último artigo da série.
Nele, trataremos em detalhes o conceito de views stateless e toda a discussão que esta questão gera. Esta é
uma solicitação antiga de muitos desenvolvedores, que viram, ainda que de forma reduzida, o seu pedido
finalmente atendido. Veremos, ainda, como funciona um mecanismo intitulado contratos de bibliotecas de
recursos, cuja ideia é a de suportar múltiplos templates dentro de uma aplicação Java para web.
Encerraremos o segundo artigo com algumas palavras sobre outra característica que, embora não tenha o
rótulo de “Big Ticket”, será de grande valor para a comunidade de desenvolvedores, facilitando
significativamente o trabalho com o recurso de upload de arquivos em aplicações web baseadas em JSF.
Desde já, gostaríamos de deixar novamente o convite para que o leitor consulte a documentação oficial da
JSR-344, cujo endereço pode ser encontrado na seção Links. Nela é possível termos uma noção mais plena
de todo o volume de trabalho realizado em cima do JSF, e o quanto tudo o que foi incorporado contribui no
sentido de entregar, à comunidade, um framework ainda mais robusto, poderoso, flexível e completo.
Por enquanto, desejamos ao leitor um bom divertimento com o conteúdo abordado até aqui, e deixamos
marcado um encontro, muito em breve, para fecharmos este nosso estudo em cima das novidades do
JavaServer Faces. Até lá!
A renovação do JSF – Parte 2
Neste artigo abordaremos três outras características centrais do
JSF 2.2, cobrindo todos os pontos classificados, na especificação,
como “Big Ticket Features”, fechando assim uma análise em torno
de todas as novidades mais relevantes.
A renovação do JSF – Parte 2
JavaServer Faces, o framework mais popular da especificação Java EE para o desenvolvimento da camada
de apresentação em sistemas web, foi atualizado. No primeiro artigo desta série, estudamos duas das mais
relevantes mudanças trazidas com a nova versão: uma perspectiva de desenvolvimento orientado a fluxos
(Faces Flow) e marcadores amigáveis à HTML 5, permitindo o uso desta linguagem em substituição à
Facelets na estruturação de views/páginas, quando desejado e/ou necessário. No texto que se segue,
abordaremos três outras características centrais do JSF 2.2, cobrindo todos os pontos classificados, na
especificação, como “Big Ticket Features”, fechando assim uma análise em torno de todas as novidades
mais relevantes encontradas nesta nova versão do framework.
Em que situação o tema é útil
Este tema será útil para todo desenvolvedor web que utilize Java como tecnologia-alvo. Será igualmente
proveitoso para todo entusiasta de tecnologias web que gosta de se manter atualizado sobre a evolução dos
frameworks mais populares do mercado ao longo do tempo.
No primeiro artigo desta série, analisamos uma característica bastante útil chamada Faces Flow. A partir
dela, passamos a contar com suporte nativo, em Java EE, para o desenvolvimento de aplicações orientadas a
fluxos, algo inédito até então e projetado a partir das melhores práticas e melhores recursos presentes em
frameworks de mercado que já trabalham neste modelo.
Estudamos, também, a flexibilização da especificação JSF no tocante à construção de interfaces com o
usuário a partir do recurso intitulado “HTML Friendly Markups” (ou, traduzido para o português,
marcadores amigáveis à HTML 5). Por ele, passa a ser possível contornarmos o modelo padrão de
desenvolvimento de views, substituindo a linguagem Facelets por HTML e, ainda assim, utilizarmos todos
os benefícios dos serviços oferecidos pelo framework no ambiente servidor (como a representação de views
a partir de árvores de componentes, preservação do estado dessas views, validações, conversões, dentre
outros).
Neste último artigo, veremos outras três importantes novidades do JSF 2.2. Ao longo do texto, o leitor
tomará contato com um novo componente projetado especificamente para a carga de arquivos (file upload),
uma estratégia para configurar e trabalhar com views stateless e, ainda, uma técnica para a construção e
utilização de múltiplos templates dentro de uma mesma aplicação.
Carregando arquivos no servidor com JSF
Carregar arquivos da camada cliente para a camada servidora nunca foi um recurso nativo no JavaServer
Faces. Para que nossas aplicações apresentassem este tipo de característica para o usuário final, tínhamos
que recorrer a dependências externas, dentre as quais a mais popular é a Apache Commons FileUpload (veja
a seção Links). Mesmo em implementações mais sofisticadas da especificação, como é o caso do
PrimeFaces, o componente que apresenta este recurso pronto para o desenvolvedor utiliza, implicitamente,
essas mesmas dependências.
Para começarmos a analisar este novo componente para carga de arquivos trazido pelo JSF 2.2, precisamos
compreender, com clareza, o ambiente no qual está inserido. Este recurso do framework tem sua base
estabelecida no suporte a multipart da Servlet API 3.0, o que significa que:
· O controlador frontal do JSF, javax.faces.webapp.FacesServlet é, agora, configurado com a anotação
@MultipartConfig da Servlet API 3.0 (JSR-315), tornando possível o tratamento de requisições cujo
conteúdo tenha sido codificado como multipart/form-data;
· Esta configuração descrita no item acima gera uma dependência do JSF em relação à Servlet API 3.0,
como já havíamos antecipado no início do artigo anterior.
Todo formulário cujo tipo de codificação de conteúdo seja multipart-form-data submeterá os dados nele
contido como uma grande stream de dados preparada de modo que cada campo seja representado como uma
parte. Toda parte, por sua vez, conterá não apenas o conteúdo definido, mas também, metadados como
tamanho, nome de arquivo (quando aplicável), dentre outros. Dentro dessa stream de dados, enviada na
requisição, são usadas strings delimitadoras de início e de fim. Assim, no ambiente servidor, será possível
separar os dados e metadados de cada campo para, então, processá-los adequadamente.
Uma parte, na Servlet API 3.0, é representada pela interface javax.servlet.http.Part. De acordo com sua
documentação oficial, esta interface representa “uma parte ou um item de um formulário recebida(o) a partir
de uma requisição cujo método seja POST e cujo tipo de encoding seja multipart/form-data”.
Sem nos demorarmos mais em teoria, vamos então para um estudo de caso. Começaremos pela Listagem 1,
que contém um formulário no qual usamos o novo componente criado para upload de arquivos, cujo
marcador atende por <h:inputFile> e pertence ao espaço de nomes de elementos HTML da JSF
(http://xmlns.jcp.org/jsf/html). Ainda sobre este formulário, é importante nos atentarmos para alguns fatos:
· Conforme requisito já levantado, a codificação de seu conteúdo foi configurada como multipart/form-data;
· O campo de carregamento de arquivo (<h:inputFile>) terá seu valor associado a um campo de um bean
gerenciado (cujo código veremos em instantes), através da já conhecida notação da Expression Language;
· Todo arquivo carregado e submetido deverá ser representado logicamente por uma instância de
javax.servlet.http.Part, dada a dependência já mencionada do JSF pela Servlet API.
Listagem 1. index.xhtml. Página inicial da aplicação que demonstra a carga de arquivos em JSF 2.2.
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html">
<h:head>
<title>Testando o upload de arquivos com JSF 2.2</title>
</h:head>
<h:body>
<h:form prependId="false" enctype="multipart/form-data" >
<h:panelGrid columns="2" >
<h:outputText value="Entre com o arquivo:" />
<h:inputFile value="#{uploadBean.arquivo}" />
<h:commandButton value="Enviar arquivo"
action="#{uploadBean.carregarArquivo()}" />
</h:panelGrid>
</h:form>
</h:body>
</html>
Observe a Listagem 2. Nela, encontramos o código-fonte do bean gerenciado citado no segundo item da
lista de marcadores do parágrafo anterior. Há um atributo nesta classe, chamado arquivo, que é exatamente
aquele usado pelo formulário para transmitir o arquivo escolhido por um usuário para carga. Este atributo,
como já sentenciado no terceiro item da lista acima, deve ter como tipo javax.servlet.http.Part, pelas
condições já explicadas.
Listagem 2.UploadBean.java. Bean gerenciado que realiza a carga de arquivos no servidor.
package br.com.devmedia.uploadapp.mbeans;
import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import javax.enterprise.context.SessionScoped;
import javax.inject.Named;
import javax.servlet.http.Part;
/**
* Bean gerenciado utilizado para exemplificar o uso do suporte nativo da
* especificação JavaServer Faces 2.2 no tratamento a upload de arquivos.
*
* @author pedrobrigatto
*/
@Named(value = "uploadBean")
@SessionScoped
public class UploadBean implements Serializable {
/**
* Atributo usado para receber o arquivo selecionado pelo usuário
*/
private Part arquivo;
/**
* Método usado pela view para submeter o formulário preenchido pelo
* usuário.
*
* @return Um outcome usado pela aplicação para determinar a próxima view.
* @throws IOException Caso algum problema na escrita do arquivo ocorra.
*/
public String carregarArquivo() throws IOException {
String nomeArquivoSubmetido = arquivo.getSubmittedFileName();
System.out.println(String.format(
"Valor da propriedade submittedFileName antes do parse: %s",
nomeArquivoSubmetido));
formatarNome(nomeArquivoSubmetido);
System.out.println(String.format(
"Valor da propriedade submittedFileName depois do parse: %s",
nomeArquivoSubmetido));
// Usando modelo anterior a Servlets API 3.1, a partir do cabeçalho.
String nomeArquivo = lerNomeArquivo(arquivo);
arquivo.write(nomeArquivo);
return "success";
}
/**
* Método usado para obter o nome do arquivo a partir de uma coleção de
* campos de cabeçalho identificado pelo nome "content-disposition".
*
* @param arquivo Parte do formulário que guarda um arquivo a ser
* armazenado.
* @return O nome do arquivo, guardando no cabeçalho.
* @throws IOException Caso algum problema de leitura desta parte do
* formulário ocorra.
*/
private String lerNomeArquivo(Part arquivo) throws IOException {
String nomeObtido = null;
String[] dadosCD = arquivo.getHeader("content-disposition").split(";");
for (String cd : dadosCD) {
if (cd.trim().startsWith("filename")) {
System.out.println(String.format("Atributo do cabeçalho: %s", cd));
// Removendo as aspas duplas que envolvem o nome do arquivo.
String nomeArquivo = cd.substring(cd.indexOf("=") + 1).
trim().replace("\"", "");
System.out.println(String.format("Valor guardado pelo atributo: %s",
nomeArquivo));
formatarNome(nomeArquivo);
nomeObtido = nomeArquivo;
break;
}
}
return nomeObtido;
}
private void formatarNome(String nomeArquivo) {
// Recuperando o texto imediatamente após a ocorrência da última barra.
nomeArquivo = nomeArquivo.substring(
nomeArquivo.lastIndexOf(File.separatorChar) + 1);
System.out.println(String.format("Atributo do cabeçalho: %s", nomeArquivo));
}
public Part getArquivo() {
return arquivo;
}
public void setArquivo(Part arquivo) {
this.arquivo = arquivo;
}
}
Esta interface é, por si só, suficiente para representar todas as informações de um arquivo, de seu conteúdo a
seu nome e tamanho. Na Java EE 6, esta interface ainda não continha um método cuja assinatura é a
seguinte:
getSubmittedFileName() : String
Este método foi incorporado na Servlet API 3.1, que é outro conjunto de melhorias/correções trazido com a
Java EE 7. Na versão anterior, era necessário implementarmos uma lógica na qual tomávamos um item de
cabeçalho específico, de identificador content-disposition e, a partir dele, verificávamos o campo de nome
filename. Atualmente, a execução do método acima exposto mostrou-se equivalente à lógica anteriormente
empregada, de modo que uma invocação a ele é, a partir de agora, suficiente para obtermos o nome do
arquivo submetido por um usuário.
De qualquer maneira, esta lógica foi inserida no código da Listagem 2, de modo que o leitor tenha acesso ao
modelo antigo de se recuperar o nome de arquivos. Quando depuramos a aplicação e analisamos este item de
cabeçalho com calma, vemos que são três os campos retornados. São eles:
· form-data: indica o tipo de codificação de conteúdo;
· name: fornece o ID do campo na árvore de componentes da view;
· filename: fornece o nome do arquivo submetido.
Usando esta abordagem, portanto, basta que percorramos este array de elementos e capturemos o valor
guardado pela célula identificada por filename. Ao encontrá-la, partimos para o processamento de seu
conteúdo, de modo a garantir que, independente do sistema operacional no qual estivermos trabalhando,
tenhamos extraído seu valor corretamente.
O processo de escrita do arquivo em disco, no ambiente servidor, é bastante simplificado com o uso da API
de javax.servlet.http.Part. Ainda na Listagem 2, vemos que a última instrução do método
carregarArquivo() consiste em uma chamada a um método desta API, cuja assinatura é a seguinte:
write (String filename) : void
sendo filename o nome do arquivo obtido de uma dentre as duas maneiras já citadas e, aqui, somente
reforçadas:
· Recuperando-se um campo definido no cabeçalho de ID content-disposition;
· Recuperando-se o nome deste arquivo por meio de uma chamada ao método
javax.servlet.http.Part#geSubmitedFileName().
Isto, por si só, é suficiente para que o arquivo seja gravado em disco, em um caminho relativo àquele
especificado na anotação @MultipartConfig. No caso da implementação de FacesServlet que usamos
(Mojarra), o caminho pré-estabelecido para arquivos é <home_do_servidor
>/glassfish/domains/<dominio>/generated/jsp.
É importante, ainda, mencionarmos que o servidor de aplicações que usamos em nossos testes é o GlassFish
4.
Com o sucesso do upload do arquivo, será apresentada ao usuário uma view de sucesso, cujo código
podemos ver na Listagem 3. Nela, também temos um comando que permite voltarmos à view da Listagem
1, para executarmos novamente todo o processo em uma nova carga.
Listagem 3. success.xhtml. Página de saída quando a carga de arquivos for bem sucedida.
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html">
<h:head>
<title>Testando o File Upload da JSF 2.2</title>
</h:head>
<h:body>
<h:form prependId="false" >
Upload realizado com sucesso!!!!!!!!!! <br /><br />
<h:commandButton value="Voltar" action="index" />
</h:form>
</h:body>
</html>
E, assim, cobrimos o recurso de upload de arquivos que o JSF passou a suportar, desde sua versão 2.2,
nativamente, como parte integrante de sua especificação. Como vimos, os primeiros passos com este novo
recurso são bastante simples de serem seguidos, ponto muito positivo para que passemos a adotá-lo em
nossas próximas soluções.
Contratos de bibliotecas de recursos
Neste tópico, analisaremos outra característica do JavaServer Faces rotulada como “Big Ticket Feature”. No
primeiro artigo da série, já vimos duas delas, a saber: Faces Flow e HTML Friendly Markups. Agora, é a vez
de falarmos do que se chamou de Resource Library Contracts, ou “contratos de bibliotecas de recursos”.
Como já sabemos, algumas características planejadas para o JSF 2.2 acabaram sendo prejudicadas por terem
sido iniciadas muito tardiamente. Em alguns casos, tais características foram totalmente postergadas,
enquanto, em outros, sofreram redução de escopo para que pudessem, ainda que sem a sofisticação
planejada, ser incorporadas na especificação. Este é o caso do que, originalmente, deveria se chamar Multi-
templating, característica muito relevante que ainda não teremos como ver funcionando em sua plenitude.
Em linhas gerais esta proposta visa permitir:
· Que se defina múltiplos templates para um mesmo projeto, empacotando-os de forma independente das
aplicações que deles façam uso;
· O suporte “on-the-fly” para mudança de temas, possibilitando a troca de fontes, diagramação e mesmo
padrão de cores de acordo com a preferência do usuário, com o sistema em pleno funcionamento;
· Que usuários criem seus próprios temas e os carreguem na aplicação, de modo que a estilização das
soluções JSF seja um processo não mais vinculado apenas a desenvolvedores, mas também ao público
consumidor de soluções baseadas em JSF.
A redução do escopo desta característica fez com que se atribuísse, ao trabalho, o título mencionado no
início deste tópico: Resource Library Contracts. Embora tenha sido entregue algo muito menor do que o
inicialmente planejado, o que temos em mãos já é, sem dúvidas, muito rico.
Tudo gira em torno do conceito de contratos. Mas, afinal, o que são? Para ajudá-lo a entender, faremos uso
da Figura 1. Todo contrato é composto, como se vê na ilustração, por três elementos:
1. Recursos: imagens, arquivos contendo páginas de estilo, dentre outros;
2. Templates: os modelos de views propriamente ditos, que estabelecem como o conteúdo será apresentado
para o usuário final;
3. Pontos de inserção: pontos, nesses modelos já citados, configurados para acomodar conteúdo das
diversas views que compõem o sistema;
Figura 1. Diagrama representando a estrutura de contratos.
Um dos pontos fortes da estratégia de definição de contratos é que cada um pode ser empacotado
separadamente dos demais. Seguindo a ilustração proposta na Figura 1, poderíamos ter três arquivos JAR
contendo, cada qual, o conteúdo correspondente a um contrato. Estes, por sua vez, poderiam ser utilizados
não apenas no projeto que veremos adiante, mas em tantos outros projetos em que pudessem ser úteis.
Apresentado o conceito, passemos para um estudo mais prático. Antes, porém, convidamos o leitor a dedicar
alguma atenção à Figura 2. Nela, temos a estrutura de um projeto criado a partir da IDE NetBeans que,
embora esteja oficialmente na versão 7.3.1, já está com a versão 7.4 Beta disponível para download. Nela,
encontramos suporte direto à criação de contratos, em que a própria IDE monta para nós a estrutura de
diretórios de acordo com a convenção especificada. Esta convenção dita que, em um projeto em Java para a
web, todo contrato deve ser criado dentro de um novo diretório denominado contracts; diz, também, que o
nome que se dá ao diretório referente ao contrato será o nome do contrato em questão. Na Figura 2, de
acordo com o que acabamos de ver, temos um exemplo de dois contratos distintos, de nomes contrato1 e
contrato2.
Figura 2. Estrutura do projeto para estudo de Contratos de Bibliotecas de Recursos do JSF.
A Listagem 4 nos apresenta o template definido para o contrato de nome contrato1. Nela, o leitor perceberá
que existe um ponto de inserção, identificado pelo nome conteudo, que será usado para, em tempo de
execução, acomodar um conteúdo de acordo com a view que for apresentada ao usuário. A Listagem 5, por
sua vez, mostra outro template, definido para o contrato de nome contrato2, que difere do primeiro apenas
na cor de fundo e em alguns poucos textos que serão apresentados ao usuário. Novamente, o mesmo ponto
de inserção, com o mesmo nome que o primeiro, aparece no template. Veremos, a seguir, que o fato do
ponto de inserção ser identificado pelo mesmo nome nos dois templates será um ponto decisivo no
comportamento da aplicação.
Listagem 4. template.xhtml. Template definido para o primeiro contrato da aplicação.
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets">
<h:head>
<title>Java Server Faces 2.2 - JAVA MAGAZINE</title>
</h:head>
<h:body style="background-color: brown;">
<p><h2>Testando o uso de templates múltiplos com JSF 2.2 - TEMPLATE 1</h2></p>
<p style="color: whitesmoke;">
<h3>Este é o primeiro contrato definido para a aplicação</h3>
</p>
<ui:insert name="conteudo" />
</h:body>
</html>
Listagem 5. template.xhtml. Template definido para o segundo contrato da aplicação.
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets">
<h:head>
<title>Java Server Faces 2.2 - JAVA MAGAZINE</title>
</h:head>
<h:body style="background-color: blueviolet;">
<p><h2>Testando o uso de templates múltiplos com JSF 2.2 - TEMPLATE 2</h2></p>
<p style="color: darkgreen;">
<h3>Este é o segundo contrato definido para a aplicação</h3>
</p>
<ui:insert name="conteudo" />
</h:body>
</html>
Passemos, agora, à análise do conteúdo da Listagem 6. Este é um exemplo da aplicação do recurso de
contratos em uma view que será, efetivamente, apresentada ao usuário. Nela, vemos o emprego de um novo
atributo incorporado no JSF 2.2, de nome contracts. Este atributo é aplicável apenas ao elemento <f:view>,
sendo compatível apenas com views escritas a partir da VDL facelets. Logo, páginas/views escritas em
HTML ou JSP não se beneficiam desta nova feature.
O valor definido para este novo atributo será usado para identificar o contrato a ser seguido. No exemplo
trazido com a Listagem 6, determinamos que o nome do contrato será especificado em tempo de execução,
de acordo com o valor que um bean gerenciado estiver guardando, em um de seus atributos, naquele
momento.
Listagem 6. Index.xhtml. View principal, que faz uso do sistema de contratos implementado.
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets">
<f:view contracts="#{templateBean.templateSelecionado}" >
<ui:composition template="/template.xhtml" >
<ui:define name="conteudo" >
<h:form prependId="false" >
<p style="font-family:Verdana,sans-serif;color: red; font-weight:
bolder;">
O primeiro template foi aplicado, por isso estamos vendo este conteúdo!
</p>
<h:commandButton id=”btnTroca” value="Trocar template"
action="#{templateBean.trocarTemplate()}" />
</h:form>
</ui:define>
</ui:composition>
</f:view>
</html>
Mais abaixo, ainda na Listagem 6, é definido o template que será empregado na composição desta view. No
caso, o nome do template é fixo, e recebeu o valor de template.xhtml.
Na Listagem 7, por sua vez, o que temos é um bean gerenciado que, como apresentamos há alguns
instantes, é responsável por definir qual será o contrato aplicado à view da Listagem 6. Trata-se da classe
TemplateBean, que possui um atributo intitulado templateSelecionado. Esta é a variável de instância que,
por meio da EL (Expression Language), teve seu valor vinculado ao atributo contracts da view.
Inicialmente, este atributo de TemplateBean está associado a uma constante estática identificada como
CONTRATO1, de valor, “contrato1”. Este valor é alternado entre os valores “contrato1” e “contrato2”
(este último guardado em outra constante, CONTRATO2) a cada vez que o usuário clicar no botão
identificado como btnTroca, da view exibida na Listagem 6.
Listagem 7. TemplateBean.java. Bean gerenciado responsável por gerenciar a mudança de templates em
runtime.
package br.com.devmedia.multitemplate.mbeans;
import java.io.Serializable;
import javax.enterprise.context.SessionScoped;
import javax.inject.Named;
/**
* Bean gerenciado responsável por realizar a troca de template da aplicação em
* tempo de execução, demonstrando a característica intitulada "Resource Library
* Contracts" da especificação JavaServer Faces 2.2.
*
* @author pedrobrigatto
*/
@Named(value = "templateBean")
@SessionScoped
public class TemplateBean implements Serializable {
private static final String CONTRATO1 = "contrato1";
private static final String CONTRATO2 = "contrato2";
private String templateSelecionado = CONTRATO1;
public String getTemplateSelecionado() {
return templateSelecionado;
}
public void setTemplateSelecionado(String templateSelecionado) {
this.templateSelecionado = templateSelecionado;
}
/**
* Método usado para se trocar o contrato em uso pelo cliente no momento.
*/
public void trocarTemplate () {
if (templateSelecionado != null) {
switch (templateSelecionado) {
case CONTRATO1:
templateSelecionado = CONTRATO2;
break;
case CONTRATO2:
templateSelecionado = CONTRATO1;
break;
}
} else {
templateSelecionado = "contrato1";
}
}
}
Este cenário é flexível ao ponto de permitir a troca, em tempo de execução, do contrato seguido pela página
em exibição. O nível de flexibilidade seria ainda maior caso a escolha do template fosse também dinâmica,
determinada por um bean gerenciado de acordo com o gosto do usuário. Quando, no exemplo apresentado,
fixamos o nome do template como template.xhtml, acabamos obrigados a nomear cada arquivo de template,
dentro de cada pasta de contrato, exatamente com este nome, de modo que a aplicação seja capaz de
encontrar, para cada contrato selecionado, o modelo a ser seguido pela página em exibição.
Por fim, apresentamos na Figura 3 o resultado da execução desta aplicação. Isto é tudo o que precisamos
saber para começar a trabalhar com múltiplos templates dentro de soluções baseadas no JSF 2.2. Embora
tudo o que vimos já seja suficiente para o desenvolvimento de soluções interessantes, é importante que o
leitor fique atento às novidades que virão na próxima edição da especificação JSF. Como dito no início, esta
é uma das tantas características incorporadas no framework com um escopo significativamente reduzido em
relação ao projeto original. É importante que fiquemos atentos, pois uma nova revisão desta especificação
trará, certamente, ainda mais poder no que tange ao recurso de múltiplos templates em Java para a web.
A seguir, vamos examinar a última das grandes características do JSF 2.2, que é o conceito de views sem
estado (stateless views).
Figura 3. Demonstração do uso de dois templates aplicados a uma mesma view.
Stateless views
Já sabemos, dos conceitos vistos no primeiro artigo desta série, que em JSF há uma preservação do estado de
cada componente e, consequentemente, da view como um todo, no ambiente servidor. Esta é uma
característica que não causa incômodo à maioria dos desenvolvedores na esmagadora maioria dos casos,
mas é constantemente foco de discussão entre outros, que levantam para debate alguns pontos mais
sensíveis, como:
1. Guardar o estado da view pode ser prejudicial para o desempenho da aplicação;
2. O consumo de memória acaba se tornando abusivo a partir de frameworks que guardam o estado de
views, uma vez que esta é uma informação adicional que, para toda view, sempre deve ser considerada;
3. Guardar o estado de views gera uma série de inconvenientes no tocante a exceções com relação às views
expiradas;
4. Manter o estado das views impede o roteamento de requisições entre máquinas servidoras executando
uma aplicação JSF em um contexto de clusters e suporte a failover.
No contexto das máquinas servidoras dos dias de hoje, é um grande exagero (e, cabe dizer, até certo
preciosismo) falar em prejuízo tanto no consumo de memória quanto no desempenho da aplicação, pois um
estado de uma view, representado no ambiente servidor, definitivamente não traz qualquer degradação para
o ambiente em si. No entanto, os outros dois pontos merecem uma atenção especial, principalmente aquele
que diz respeito à questão do ambiente de failover. Portanto, dedicaremos um foco maior sobre esses dois
itens a partir de agora.
Sobre expiração de views
Sempre que uma página é requisitada por um usuário e, por algum motivo, ela não se encontra mais salva na
sessão, podemos nos deparar com uma situação de view expirada, sinalizada pela runtime do JSF como uma
instância de javax.faces.application.ViewExpiredException. Um exemplo clássico em que isso acontece é
aquele em que o sistema permaneceu ocioso por um longo tempo, excedendo o tempo máximo de sessão
configurado.
O processo é razoavelmente simples de entender, e falaremos um pouco sobre ele antes de prosseguirmos. Já
abordamos o ciclo de vida de uma requisição JSF em detalhes no primeiro artigo, e focaremos mais
especificamente na primeira daquelas etapas, conhecida como “Restauração de uma view”. Este é o
momento em que casos de expiração de views podem ocorrer.
Tudo começa quando uma view precisa ser recuperada (montada ou restaurada), processo conduzido pela
instância de javax.faces.application.ViewHandler mantida pelo singleton
javax.faces.application.Application, que representa a aplicação propriamente dita. O método invocado,
aqui, é o initView(FacesContext), processado antes mesmo do ciclo de vida JSF ser iniciado.
Caso esta requisição seja do tipo postback (ou seja, trata-se de uma view já visitada anteriormente, à qual se
está retornando), o fluxo segue em frente a partir de uma tentativa, pela já citada instância de ViewHandler,
de restaurar a view, o que é executado a partir da invocação do método restoreView(FacesContext
contexto, String viewId), em que viewId é o identificador da view envolvida na requisição corrente.
O retorno deste método é uma instância de javax.faces.component.UIViewRoot, que representa a árvore de
componentes da view identificada pelo ID fornecido. Sempre que, entretanto, obtivermos como resultado da
execução deste método um ponteiro nulo, estaremos diante de um caso em que a view desejada não pôde ser
encontrada. Este é um caso que, pela especificação, estaremos sujeitos ao lançamento de uma instância da
classe javax.faces.application.ViewExpiredApplication.
Nos casos em que a requisição não é de um postback, haverá a tentativa de se recuperar a instância de
javax.faces.view.ViewDeclarationLanguage da view desejada. Esta busca começa a partir de uma
chamada a um método da instância de ViewHandler de assinatura deriveLogicalViewId(FacesContext
contexto, String rawViewID), cujo papel é o de retornar o identificador da view a partir de um valor bruto
passado pela requisição que, quando submetido a um algoritmo interno de ViewHandler, resulta no
identificador da view em questão. Este algoritmo não será detalhado aqui, mas pode ser encontrado na
documentação oficial da JSR-344 (cujo endereço se encontra na seção Links).
De posse do identificador da view, o próximo passo é obter a instância de ViewDeclarationLanguage a
partir dele, o que é feito invocando-se o método, também da instância de ViewHandler, chamado
getViewDeclarationLanguage(FacesContext contexto, String viewId).
Se nenhuma instância de ViewDeclarationLanguage puder ser obtida pela chamada ao método
mencionado, o processo de renderização da resposta será imediatamente acionado. Caso contrário, haverá
uma sequência de passos (identificação de metadados, criação de uma view a partir desses metadados,
dentre outros) que culminarão na criação da view desejada e, por fim, na execução da fase de renderização
da resposta.
De uma forma ou de outra, o ponto importante aqui é que, dados contextos de longo período da aplicação
em modo de repouso (ou seja, um intervalo de tempo razoavelmente longo em que, muito embora a
aplicação esteja aberta em um navegador, não esteja sendo usada) ou mesmo um longo período em que uma
view já visitada não é novamente apresentada ao usuário, teremos configurados cenários em que a view em
questão pode se encontrar expirada.
Nessas situações, invariavelmente, uma exceção será lançada e, em última instância, terá que ser tratada
devidamente para não resultar em uma experiência frustrante para o usuário. Este é um problema que surge
exatamente pelo fato de que o JSF é um framework web que, como tantos outros, preocupa-se com a
preservação do estado da view em ambiente servidor.
Existem muitos pontos positivos nesta abordagem, sendo que a esmagadora maioria dos desenvolvedores
não apresenta queixa alguma sobre este recurso. Entretanto, uma parcela da comunidade vem questionando,
frequentemente e há um bom tempo, os motivos pelos quais não se incluía o suporte a views sem estado no
framework. Com o JSF 2.2, após a longa insistência e, também, a força do argumento associado à expiração
de views, temos finalmente um primeiro passo caminhando neste sentido.
Sobre ambientes rodando em cluster
Em ambientes tolerantes a falhas, é muito comum trabalharmos com clusters de servidores e aplicarmos
algoritmos de balanceamento de carga entre eles. Dessa maneira, garantimos que o sistema estará sempre
disponível a seus usuários, independentemente da carga de requisições que se aplique a ele.
Em JSF, ao guardarmos o estado de uma view na sessão, criamos um vínculo entre ela e o nó (máquina
servidora) que a gerou. Logo, quando o usuário retorna à view já criada, é obrigatório que isso seja feito pelo
mesmo nó que a originou.
Se este nó, por algum motivo, estiver indisponível, estaremos diante de um problema. Da mesma forma,
caso este nó esteja sobrecarregado e esta view seja novamente acessada por um usuário, estaremos
impedidos de trabalhar com balanceamento de carga e poderemos, por isso, ter que enfrentar problemas
como lentidão, indisponibilidade, dentre outros.
Remover nós dinamicamente de um cluster também pode ser muito complicado quando temos views que
guardam estado, pelos mesmos motivos já levantados.
Embora haja contornos possíveis para os dois problemas acima mencionados, nunca resolverão, de fato, o
problema. Logo, encontramos neles justificativas razoáveis para o suporte a views sem estado.
A solução para esta necessidade levantada pela comunidade foi obtida às pressas e incorporada tardiamente
na especificação. Embora fosse um recurso programado para ser apresentado a partir do JSF 2.2, os
trabalhos em torno desta característica começaram com muito atraso e sério risco de postergação. No
entanto, um desenvolvedor de nome Manfred Riem acabou adicionando uma solução à implementação de
referência da especificação JSF (Mojarra) de extrema simplicidade e grande valor, que consiste basicamente
nos pontos listados a seguir:
· A tag handler para <f:view> agora passa a processar o atributo transient, que já existia em versões
anteriores do JSF, passando o valor nela guardado para a raiz da árvore de componentes (UIViewRoot)
através de uma chamada ao método UIViewRoot#setTransient(boolean transient);
· Sempre que a raiz da árvore de componentes da view sendo requisitada tiver o seu valor de transiência
definido como true (o que passa a ser interpretado como uma view sem estado), a view não será restaurada.
Esta implementação de Manfred, de tão simples, limpa e eficaz, foi proposta para padronização dentro da
especificação do JSF. O grupo de trabalho da JSR-344 realizou apenas algumas poucas adaptações no
sentido de acomodar toda a lógica no contexto do framework e, finalmente, lançou o recurso tanto na
especificação quanto na implementação de referência do framework.
Naturalmente, há muito espaço para melhorias. Atualmente, a propriedade transient deve ser definida como
true em cada view que quisermos definir como stateless. A propriedade, no entanto, aplica-se a todos os
componentes que constituem uma view, o que pode ser genérico demais em alguns cenários que
desenvolvedores enfrentam no dia a dia. De qualquer forma, o que temos na versão 2.2 já é bastante útil e
interessante, cabendo a nós esperar pela próxima revisão para aproveitar deste recurso de uma maneira mais
plena.
Conclusão
Chegamos ao final de nosso estudo sobre o JSF 2.2. Ao longo desta série de dois artigos, abordamos os
principais recursos inseridos nesta nova revisão da especificação, conhecidos como as Big Ticket Features.
Neste último artigo, especificamente, comprovamos o quanto se tornou simples trabalhar com alguns
recursos muito úteis dentro do desenvolvimento web, como o uso dinâmico de templates variados, a carga
de arquivos e, também, o uso de views stateless.
Todas essas modificações passam um recado bastante claro de que não apenas JSF, mas a plataforma Java
EE em sua plenitude, vem se tornando cada vez mais completa e, principalmente, aderente ao panorama
contemporâneo de desenvolvimento de aplicações web. Desta maneira, figura hoje como um forte candidato
a substituir, definitivamente, alguns frameworks que acabaram surgindo exatamente para cobrir deficiências
que, por anos, foram marcantes na especificação Java EE.
Estes, por não estarem vinculados a um processo tão complexo como o proposto pelo JCP, evoluem em um
ritmo naturalmente mais acelerado, o que também acaba sendo benéfico para o ecossistema como um todo,
ditando o ritmo do progresso e contribuindo para o alinhamento de pensamentos e o clareamento da ideia
por trás de cada demanda da comunidade. Veja, na seção Links, um bom artigo que apresenta uma proposta
de substituição do framework Spring por recursos presentes na própria Java EE, que é uma leitura que vem
de encontro a isto que acabamos de citar aqui.
Falando especificamente de JavaServer Faces, a marcha de seu progresso só não foi mais forte devido,
infelizmente, a algumas intempéries ocasionadas por todas as notícias veiculadas recentemente sobre
vulnerabilidades da tecnologia Java, que mobilizaram boa parte das equipes de engenheiros,
desenvolvedores e arquitetos no sentido de acelerar uma resposta frente aos problemas detectados. A
resposta, de fato, foi consistente e veio em um intervalo muito curto de tempo, mas trouxe consigo um
inevitável efeito colateral refletido no atraso de alguns dos cronogramas internos.
Entretanto, os planos da especificação continuam sólidos, e tudo isso nos faz esperar, para uma próxima
revisão, soluções definitivas para todas as características que, neste momento, ainda se mostram
timidamente, nos limites do que seus escopos, hoje um pouco reduzidos, nos permitem enxergar.
Desejamos a todos os leitores um bom divertimento e uma trajetória de muito sucesso e muitos e bons frutos
ao adotarem todos esses recursos do JSF 2.2 em seus projetos em Java para a Web.