O JSF (JavaServer Faces) é um framework que segue o padrão MVC para construção de interfaces de usuário. O JSF possui seis versões (JSF 1.0, JSF 1.1, JSF 1.2, JSF 2.0, JSF 2.1 e JSF 2.2). A última versão introduziu diversos novos conceitos e funcionalidades. Além disso, tivemos a atualização de diversos componentes provendo uma maior produtividade para os desenvolvedores.

Através do JSF podemos criar páginas web com um conjunto de componentes de interface com o usuário, que são reutilizáveis, que seguem o padrão Model-View-Controller (MVC), que ligam os componentes de interface com o usuário ao modelo no lado servidor, gerenciam a navegação entre as páginas em resposta a eventos da interface do usuário e ao modelo de interações, gerenciam o estado de componentes da interface com o usuário através de requisições do servidor, são de fácil construção e reuso de componentes de interface com o usuário customizados, entre diversas outras características.

No JSF 2.2 o deployment descriptor web.xml e o arquivo de configuração faces-config são opcionais. O JSF ainda disponibiliza aos programadores um conjunto de Componentes UI (User Interface), Managed Beans, Converters e Listeners customizados.

O JSF surgiu na versão 1.0 em meados de 2004 com uma especificação inicial, logo após também em 2004 surgiu a versão JSF 1.2 com diversas melhorias na API. A versão 2.0 do JSF surgiu em 2009 sendo considerada a melhor versão do JSF com diversas facilidades no uso da API, funcionalidades melhoradas e um desempenho superior às anteriores. Em 2010 surgiu a JSF 2.1 como uma versão de correção da versão 2.0. Por fim, em 2013 o JSF 2.2 surgiu com diversos novos conceitos, atualizações de versões anteriores e adição de novas funcionalidades.

Nas próximas seções veremos algumas novidades do JSF 2.2 com mais profundidade. Entre eles estudaremos as novidades introduzidas no Composite Components, Ajax, HTTP GET, Validating Data e Navegation Rules.

Composite Components

Usando as características dos Facelets e do Gerenciamento de recursos, o JSF define o “Composite Components” como um componente que consiste de um ou mais componentes JSF definido num arquivo de marcação Facelet.

O arquivo “.xhtml” está dentro de uma biblioteca de recurso. Isto permite criarmos um componente reutilizável a partir de qualquer região de uma página.

Assim sendo, o Composite Component é definido numa "página de definição" e usado em uma "página de uso" que fará uso da “página de definição”.

A “página de definição” define o metadata ou parâmetros usando <cc:interface>. Na “página de definição” também definimos a implementação utilizando a tag <cc:implementation>. O cc indica o prefixo para o namespace http://xmlns.jcp.org/jsf/composite/.

Aqui temos mais uma vantagem do JSF 2.2 em relação ao JSF 1.2 que visa facilitar a vida dos desenvolvedores. Enquanto no JSF 1.2 o desenvolvedor tinha que saber em profundidade como funcionava o ciclo de vida do JSF para criar um Composite Component, agora podemos apenas usar um arquivo XHTML para isto.

Na Listagem 1 temos um exemplo de um Facelet com fragmento de código de uma tela de login.

Listagem 1. Exemplo de uma tela de login.


  <h:form>
           <h:panelGrid columns="3">
                     <h:outputText value="Nome:" />
                     <h:inputText value="#{user.nome}" id="nome"/>
                     <h:message for="nome" style="color: red" />
                     <h:outputText value="Senha:" />
                     <h:inputText value="#{user.senha}" id="senha"/>
                     <h:message for="senha" style="color: red" />
           </h:panelGrid>
   
           <h:commandButton actionListener="#{userService.registrar}"
                     id="loginButton"
                     action="status"
                     value="submit"/>
  </h:form>
  

A tela possui label “Nome” e outra “Senha” e dois campos de texto ao lado e um botão para submeter os dados.

Se este formulário de login deverá ser exibido em múltiplas páginas seria interessante convertermos este fragmento de código em um Composite Component ao invés de repetirmos este código em diversas páginas. Dessa forma, devemos copiar o fragmento de código para um arquivo “.xhtml” e o arquivo é copiado para uma biblioteca no diretório padrão de recursos.

Segue na Listagem 2 o código acima copiado para “login.xhtml” no diretório resources/minhaempresa. Esta é a “página de definição”.

Listagem 2. Exemplo de uma página de definiçã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:cc="http://xmlns.jcp.org/jsf/composite"
           xmlns:h="http://xmlns.jcp.org/jsf/html">
   
           <!-- INTERFACE -->
           <cc:interface>
           </cc:interface>
   
           <!-- IMPLEMENTATION -->
           <cc:implementation>
                     <h:form>
                              <h:panelGrid columns="3">
                                        <h:outputText value="Nome:" />
                                        <h:inputText value="#{user.nome}" id="nome"/>
                                        <!-- . . . -->
                     </h:form>
           </cc:implementation>
  </html>
  

A tag <cc:interface> define o metadata que descreve as características do componente, tais como: atributos suportados, facets e anexos para eventos dos listeners. A tag <cc:implementation> indica a marcação substituída pelo Composite Component. O namespace do Composite Component é construído concatenando o “http://xmlns.jcp.org/jsf/composite/” e “minhaempresa”.

O nome da tag é o nome do arquivo sem o sufixo “.xhtml” na "página de uso". Segue na Listagem 3 um exemplo.

Listagem 3. Exemplo de uma página de uso.


  <html xmlns="http://www.w3.org/1999/xhtml"
           xmlns:mc="http://xmlns.jcp.org/jsf/composite/mycomp"
   
           <!-- . . . -->
           <mc:login/>
  </html>
  

Vale ressaltar que necessitamos passar um valor diferente nas expressões (ao invés de #{user.nome}) e invocar diferentes métodos (ao invés de #{userService.registrar}) quando submetemos o formulário em uma página diferente. A página de definição pode passar os valores da mesma forma que da Listagem 4.

Listagem 4. Exemplo de como passar valores.


  <!-- INTERFACE -->
  <cc:interface>
           <cc:attribute name="nome"/>
           <cc:attribute name="senha"/>
           <cc:attribute name="actionListener"
                     method-signature="void action(javax.faces.event.Event)"
                     targets="ccForm:loginButton"/>
  </cc:interface>
   
  <!-- IMPLEMENTATION -->
  <cc:implementation>
           <h:form id="ccForm">
                     <h:panelGrid columns="3">
                              <h:outputText value="Nome:" />
                              <h:inputText value="#{cc.attrs.nome}" id="nome"/>
                              <h:message for="nome" style="color: red" />
                              <h:outputText value="Senha:" />
                              <h:inputText value="#{cc.attrs.senha}"
                                        id="senha"/>
                              <h:message for="senha" style="color: red" />
                     </h:panelGrid>
   
                     <h:commandButton id="loginButton"
                              action="status"
                              value="submit"/>
           </h:form>
  </cc:implementation>
  

Neste código temos todos os parâmetros explicitamente especificados em <cc:interface>. O terceiro parâmetro tem um atributo de destino se referindo a ccForm:loginButton.

Já em <cc:implementation> temos:

  • h:form tem um atributo id. Isto é necessário para que o botão dentro do formulário possa ser explicitamente referenciado.
  • h:inputText está usando #{cc.attrs.xxx} ao invés de #{user.xxx}. #{cc.attrs} é uma expressão EL padrão que está disponível para os criadores de um Composite Component e provê acesso a atributos do Composite Component atual.

Neste caso #{cc.attrs} tem um nome e senha definidos como atributos.

  • actionListener é um ponto de anexo para um evento listener. Ele é definido como uma assinatura de um método e descreve a assinatura do método.
  • h:commandButton tem um atributo id e está dentro de um h:form.

O usuário, senha e actionListener são passados por atributo sendo requeridos na página de utilização. Segue na Listagem 5 um exemplo.

Listagem 5. Passando valores de um bean por parâmetro para login.


           <ez:login
                     name="#{user.name}"
                     password="#{user.password}"
                     actionListener="#{userService.register}"/>
  

Agora a “página de utilização” pode passar diferentes backing beans, e diferentes métodos de negócio podem ser invocados quando o botão de submitt é clicado.

De forma geral um Composite Components provê os seguintes benefícios:

  • Aumentar o reuso e diminuir a replicação permitindo que códigos que podem ser repetidos estejam em um único arquivo.
  • Permitir aos desenvolvedores criar novos componentes, sem qualquer código Java ou configuração XML.

Ajax

JSF provê suporte nativo para adicionarmos Ajax nas páginas web. Isto permite um processamento parcial da View, onde apenas alguns componentes da View são usados para processamento da resposta. Ajax também permite processamento parcial da página, onde alguns componentes da página, ao contrário de toda a página, são processados.

Existem duas formas de habilitar o Ajax nas páginas:

  • Programaticamente usando Javascript;
  • Declarativamente usando a tag f:ajax.

Programaticamente a integração com Ajax é habilitada através do mecanismo de gerenciamento de recursos.

O recurso “jsf.js” na biblioteca javax.faces contém a API Javascript que facilita a interação entre o Ajax e as páginas JSF.

Para tornarmos o recurso disponível nas páginas usamos a tag “outputScript” conforme exemplificada na Listagem 6.

Listagem 6. Utilizando um outputScript e a biblioteca Javascript para tornar o Ajax disponível.


  <h:body>
           <!-- . . . -->
           <h:outputScript
                     name="jsf.js"
                     library="javax.faces"
                     target="body"/>
           <!-- . . . -->
  </h:body>
  

Para fazermos uma requisição assíncrona para o servidor utilizamos o código da Listagem 7.

Listagem 7. Fazendo uma solicitação assíncrona com Ajax.


  <h:form prependId="false">
           <h:inputText value="#{user.nome}" id="nome"/>
           <h:inputText value="#{user.senha}" id="senha"/>
           <h:commandButton value="Login"
                     type="button"
                     actionListener="#{user.login}"
                     onclick="jsf.ajax.request(this, event, {execute:
                     'nome senha', render: 'status'}); return false;"/>
           <h:outputText value="#{user.status}" id="status"/>
  </h:form>
  

Neste código dois campos de entrada (input) recebem um username e uma senha, e o terceiro campo exibe o status (se o usuário está logado ou não).

O formulário tem um “prependId” configurado para falso, assegurando que o id de cada elemento é preservado conforme foram configurados no formulário. Caso contrário, o JSF anexa o id do formulário no id de seus filhos. O “command button” tem um actionListener identificando o método no backing bean que será invocado quando o botão for clicado. Ao invés de termos o processamento de uma resposta e após isso a exibição de uma página diferente, o jsf.ajax.request envia uma requisição assíncrona para o servidor. Esta requisição é feita no “command button” no evento do clique. O execute é uma lista de componentes de entrada (input) cujos métodos setters do bean são invocados, e o render é uma lista de componentes que necessitam ser processados após a resposta assíncrona ser recebida.

A possibilidade de processar apenas parte da View como os elementos "nome" e "senha" é referenciado como o processamento parcial da View.

Similarmente, processar apenas parte da resposta da página, como o elemento "status" no exemplo anterior, é referenciado como processamento parcial da saída.

Segue na Tabela 1 uma lista dos possíveis valores do atributo render.

Valor

Descrição

@all

Todos os componentes da página.

@none

Nenhum componente na página; Este é o valor default.

@this

Elemento que acionou a requisição.

@form

Todos os componentes dentro do form.

IDs

Identificadores dos componentes separados por espaços.

EL expression

Expressão EL que determina uma coleção de string.

Tabela 1. Valores suportados pelo atributo render.

O valor padrão para o atributo execute é @this.

O bean do usuário tem campos, setters e getters e um método de negócio (Listagem 8).

Listagem 8. Exemplo de um bean do usuário.


  @Named
  @SessionScoped
  public class User implements Serializable {
           private String nome;
           private String senha;
           private String status;
           . . .
   
           public void login(ActionEvent evt) {
                     if (nome.equals(senha))
                              status = "Login efetuado com sucesso";
                     else
                              status = "Login inválido";
           }
  }
  

Podemos notar a assinatura do método login. Ele deve retornar void e receber um javax.faces.event.ActionEvent como o único parâmetro.

A integração com Ajax de forma declarativa é habilitada via f:ajax. Esta tag pode ser aninhada dentro de um único componente (permitindo Ajax para este único componente), ou pode ser "envolvido" através de múltiplos componentes (permitindo ajax para muitos componentes).

O código anterior pode ser atualizado para usar este novo estilo de Ajax (Listagem 9).

Listagem 9. Utilizando a tag f:ajax.


  <h:form prependId="false">
           <h:inputText value="#{user.nome}" id="nome"/>
           <h:inputText value="#{user.senha}" id="senha"/>
           
           <h:commandButton value="Login"
                     type="button"
                     actionListener="#{user.login}">
   
                     <f:ajax execute="nome senha" render="status"/>
   
           </h:commandButton>
   
           <h:outputText value="#{user.status}" id="status"/>
  </h:form>
  

Neste código usamos a tag <f:ajax> para especificar a lista de elementos de entrada (input) usando o atributo execute, e os elementos de saída para serem processados através do atributo render.

Por default, se <f:ajax> é aninhado dentro de um único componente e nenhum evento é especificado, a requisição assíncrona é acionada com base no evento padrão para o componente pai (no exemplo acima será o evento onclick do command button).

Também podemos especificar um atributo delay na tag <f:ajax>. Este atributo recebe um valor em milissegundos. Se várias solicitações são emitidas antes de decorrido o tempo de espera, apenas o pedido mais recente é enviado e todos os outros são descartados. Segue na Listagem 10 o exemplo de utilização do atributo no f:ajax especificando um delay de 200 milissegundos.

Listagem 10. Exemplo de f:ajax com delay.


        <f:ajax delay="200" ...>
                     . . .
           </f:ajax>
  

O valor padrão é de 300 milissegundos.

A tag <f:ajax> também pode estar envolvida com múltiplos componentes conforme mostrado no código da Listagem 11.

Listagem 11. Tag f:ajax com múltiplos componentes.


  <f:ajax listener="#{user.checkFormat}">
           <h:inputText value="#{user.name}" id="name"/>
           <h:inputText value="#{user.password}" id="password"/>
  </f:ajax>
  

Neste código f:ajax tem um atributo listener e um método Java correspondente:


         public void checkFormat(AjaxBehaviorEvent evt) {
                     //. . .
           }

Este método no listener será invocado nos eventos default para os elementos filhos de <f:ajax>. Neste caso o evento Change para h:inputText.

Podemos também especificar funcionalidades Ajax adicionais nos elementos filhos usando a tag <f:ajax> aninhada.

HTTP GET

O JSF provê suporte para o mapeamento de parâmetros da URL em requisições HTTP GET para uma EL. Além disso, também provê suporte para gerar URLs GET amigáveis.

Para mapear parâmetros da URL em requisições GET para uma EL utilizando os parâmetros da View, basta adicionarmos o seguinte fragmento de código da Listagem 12 para uma página Facelet.

Listagem 12. Exemplo de como receber um parâmetros da solicitação.


  <f:metadata>
           <f:viewParam name="nome" value="#{user.nome}"/>
  </f:metadata>
  

Ao acessarmos uma aplicação Web através da URL “index.xhtml?nome=Higor” teremos a seguinte situação:

  • Recebemos o parâmetro da solicitação pelo atributo “name” que neste exemplo é “nome”.
  • Converte e valida se necessário. Isto é feito quando um f:converter e f:validator estão aninhados, utilizados apenas com h:inputText.

Segue na Listagem 13 um exemplo de como poderíamos fazer isto.

Listagem 13. Converter e validar o que for necessário


           <f:metadata>
                     <f:viewParam name="nome" value="#{user.nome}">
                              <f:validateLength minimum="1" maximum="5"/>
                     </f:viewParam>
           </f:metadata>
  

Se tudo for feito com sucesso, o parâmetro é ligado ao #{user.nome}.

Também podemos processar os parâmetros da View antes da página ser processada usando f:event. O exemplo da Listagem 14 demonstra como poderíamos fazer isto.

Listagem 14. Utilizando f:event para fazer um pré-processamento.


  <f:metadata>
           <f:viewParam name="nome" value="#{user.nome}">
                     <f:validateLength minimum="1" maximum="5"/>
           </f:viewParam>
   
           <f:event type="preRenderView" listener="#{user.processar}"/>
   
  </f:metadata>
  

O método #{user.processar} pode ser usado para executar qualquer inicialização prioritária requerida para processar a página.

Outra funcionalidade provida pelo JSF 2.2 são as URLs GET amigáveis que são obtidas utilizando h:link e h:button. Para isto especificamos a página Facelet desejada ao invés de construirmos a URL manualmente. Segue na Listagem 15 um exemplo.

Listagem 15. Construindo URLs amigáveis no JSF 2.2 com <h:link>.


  <h:link value="Login" outcome="login"/>
  

O código acima será traduzido no seguinte código HTML (Listagem 16):

Listagem 16. Tag <h:link> anterior traduzida para HTML.


  <a href=".../faces/login.xhtml">Login</a>
  

Parâmetros para a View também podem ser facilmente especificados, conforme mostrado no exemplo da Listagem 17.

Listagem 17. Passando parâmetros para a View.


  <h:link value="Login" outcome="login">
           <f:param name="nome" value="#{user.nome}"/>
  </h:link>
  

Se no código acima #{user.nome} estiver vinculado a "Higor", então este fragmento de código seria traduzido para a seguinte tag HTML (Listagem 18):

Listagem 18. Código JSF anterior traduzido para o HTML.


  <a href=".../faces/login.xhtml?nome=Higor">Login</a>
  

A tag <h:button> também pode ser utilizada, conforme mostra a Listagem 19. Listagem 19. Utilizando h:button com URLs amigáveis.


  <h:button value="login"/>
  

Este código acima irá gerar a tag HTML da Listagem 20.

Listagem 20. Código anterior traduzido em HTML.


  <input  type="button"
           onclick="window.location.href='/JSFSample/faces/index.xhtml'; return false;"
           value="login" />
  

Validating Data

Além de podermos usar validadores embutidos e criar nossos validadores customizados, podemos ainda especificar restrições definidas no becking bean usando Bean Validation.

O código da Listagem 21 exemplifica como poderíamos fazer isso num formulário com diversos campos de texto e um botão para submetê-lo:

Listagem 21. Exemplo de um Formulário com diversos campos de texto e um botão para submetê-lo.


  <h:form>
           Nome: <h:inputText value="#{meuManagedBean.nome}"/>
           Idade: <h:inputText value="#{meuManagedBean.idade}"/>
           CEP: <h:inputText value="#{meuManagedBean.cep}"/>
   
           <h:commandButton value="Enviar"/>
   
  </h:form>
  

Todos os campos de texto estão ligados a uma propriedade do Managed Bean que tem pelo menos uma anotação do Bean Validation. Segue na Listagem 22 um exemplo de um Managed Bean mostrando algumas anotações do Bean Validation nos campos ligados ao formulário anterior.

Listagem 22. Managed Bean com anotações Bean Validation.


  @Named
  @SessionScoped
  public class MeuManagedBean implements Serializable {
           @Size(min = 3, message = "Pelo menos três caracteres são necessários!")
           private String nome;
   
           @Min(18)
           @Max(25)
           private int idade;
   
           @Pattern(regexp = "[0-9]{8}")
           private String cep;
   
           //. . .
   
  }
  

Todo elemento <h:inputText> que é suportado por um componente UIInput tem uma instância de um validador com um id javax.faces.Bean anexado a ele. O método validador é chamado durante a fase de validação no ciclo de vida do JSF.

O validador padrão javax.faces.Bean também assegura que toda violação a uma restrição é empacotado em um FacesMessage e adicionado a um FacesContext. Esta mensagem que está no FacesMessage é exibida para o usuário, assim como outras mensagens de validação são manipuladas.

Um ou mais grupos de validação pode ser associados com uma tag inputText. O exemplo da Listagem 23 mostra como isso poderia ser feito.

Listagem 23. Exemplo de grupos de validação nas tags inputText.


  Nome:
  <h:inputText value="#{meuManagedBean.nome}" id="nome">
           <f:validateBean validationGroups=
                     "org.sample.Page1Group, org.sample.OtherGroup"/>
  </h:inputText>
   
  <h:commandButton action="index2" value="Next >"/>
  

Os grupos de validação também podem ser associados com um grupo de tags inputText. O exemplo da Listagem 24 mostra como poderíamos fazer isto.

Listagem 24. Grupos de validação associados com grupos de tags.


  <f:validateBean validationGroups="org.sample.MyGroup">
           <h:inputText value="#{meuManagedBean.nome}"/>
           <h:inputText value="#{meuManagedBean.idade}"/>
  </f:validateBean>
  

Neste código as restrições são validadas pelos campos identificados por #{meuManagedBean.name} e #{meuManagedBean.age}.

Navigation Rules

O JSF 2.2 define regras de navegação explicitas e implícitas.

Regras de navegação implícitas procuram por um resultado de uma ação como, por exemplo, um clique num link ou num botão. Se uma página Facelet corresponder com o resultado de uma ação, ou seja, existe um Facelet com o mesmo valor da ação, então esta página será processada. Segue na Listagem 25 um exemplo de um command Button com uma ação especificada:

Listagem 25. Exemplo de um command Button com uma ação definida.


  <h:commandButton action="login" value="Login"/>
  

Neste código, quando clicarmos no botão seremos renderizados para a página login.xhtml que está no mesmo diretório.

As navegações explícitas são definidas usando <navigation-rule> no faces-config.xml e podemos especificar navegações condicionais usando <if>. O código da Listagem 26 demonstra um exemplo de sua utilização.

Listagem 26. Exemplo de uma navegação explícita


  <navigation-rule>
           <from-view-id>/index.xhtml</from-view-id>
   
           <navigation-case>
                     <from-outcome>success</from-outcome>
                     <to-view-id>/login.xhtml</to-view-id>
                     <if>#{user.isPremium}</if>
           </navigation-case>
  </navigation-rule>
  

Neste código, a página de navegação “index.xhtml” vai para “login.xhtml” apenas se o usuário for um cliente “Premium”.

Podemos concluir que as novidades introduzidas visam aumentar a produtividade dos desenvolvedores e corrigir situação que eram um pouco desagraveis nas versões anteriores. É sempre importante os desenvolveres manterem contato com a comunidade de desenvolvimento JSF mostrando pontos que poderiam ser melhorados ou adicionados. Através desse feedback as versões continuam melhorando cada vez mais e se adequando ao que é considerado ideal pelos desenvolvedores.

Bibliografia

[1]Josh Juneau. Java EE 7 Recipes: A Problem-Solution Approach. Apress, 2013.

[2]Josh Juneau. Introducing Java EE 7: A Look at What's New. Apress, 2013.

[3]Arun Gupta. Java EE 7 Essentials. O'Reilly, 2013.