2. Java Server Faces
Apresentamos agora o framework Java Server Faces. Será utilizada a versão 2, mas os exemplos apresentam principalmente características da versão 1. Apresentaremos, da versão 2, apenas as características necessárias para o aplicativo de exemplo que se seguirá.
2.1. O papel do JSF em um aplicativo web
Primeiramente, vamos situar o JSF no desenvolvimento de um aplicativo web. Na maioria das vezes, ele será construído sobre uma arquitetura multicamadas, como a seguinte:
![]() |
- a camada [web] é a camada em contato com o usuário da aplicação web. O usuário interage com a aplicação web por meio de páginas da web visualizadas por um navegador. É nessa camada que se situa JSF e exclusivamente nessa camada,
- a camada [métier] implementa as regras de gestão da aplicação, tais como o cálculo de um salário ou de uma fatura. Essa camada utiliza dados provenientes do usuário por meio da camada [web] e do SGBD por meio da camada [DAO],
- a camada [DAO] (Data Access Objects), a camada [jpa] (Java Persistence API) e o driver JDBC gerenciam o acesso aos dados do SGBD. A camada [jpa] atua como um ORM (Mapeador Objeto-Relacional). Ela faz a ponte entre os objetos manipulados pela camada [DAO] e as linhas e colunas dos dados de um banco de dados relacional,
- a integração das camadas pode ser realizada por um contêiner Spring ou por um EJB3 (Enterprise Java Bean).
Os exemplos apresentados a seguir para ilustrar o JSF utilizarão apenas uma camada, a camada [web]:
![]() |
Depois de adquirir os conceitos básicos do JSF, construiremos aplicativos Java EE multicamadas.
2.2. O modelo de desenvolvimento MVC de JSF
JSF implementa o modelo de arquitetura conhecido como MVC (Modelo – Visão – Controlador) da seguinte maneira:
![]() |
Essa arquitetura implementa o Padrão de Design MVC (Modelo, Visão, Controlador). O processamento de uma solicitação de um cliente ocorre de acordo com as quatro etapas a seguir:
- solicitação – o navegador do cliente envia uma solicitação ao controlador [Faces Servlet]. Este recebe todas as solicitações dos clientes. É a porta de entrada do aplicativo. É o C de MVC,
- processamento — o controlador C processa essa solicitação. Para isso, ele conta com a ajuda de gerenciadores de eventos específicos da aplicação [2a]. Esses gerenciadores podem precisar da ajuda da camada de negócios [2b]. Uma vez processada a solicitação do cliente, ela pode gerar diversas respostas. Um exemplo clássico é:
- uma página de erros, caso a solicitação não tenha podido ser processada corretamente;
- uma página de confirmação, caso contrário,
- navegação — o controlador escolhe a resposta (= vista) a ser enviada ao cliente. Escolher a resposta a ser enviada ao cliente envolve várias etapas:
- escolher o Facelet que irá gerar a resposta. É o que chamamos de vista V, o V de MVC. Essa escolha depende, em geral, do resultado da execução da ação solicitada pelo usuário;
- fornecer a esse Facelet os dados de que ele precisa para gerar essa resposta. Na verdade, ela contém, na maioria das vezes, informações calculadas pelo controlador. Essas informações formam o que chamamos de modelo M da vista, o M de MVC,
A etapa 3 consiste, portanto, na escolha de uma visualização V e na construção do modelo M necessário para ela.
- Resposta — o controlador C solicita que a Facelet escolhida seja exibida. Esta utiliza o modelo M preparado pelo controlador C para inicializar as partes dinâmicas da resposta que deve enviar ao cliente. A forma exata dessa resposta pode variar: pode ser um fluxo HTML, PDF, Excel, ...
Em um projeto JSF:
- o controlador C é o servlet [javax.faces.webapp.FacesServlet]. Ele se encontra na biblioteca [javaee.jar],
- as visualizações V são implementadas por páginas que utilizam a tecnologia Facelets,
- os modelos M e os gerenciadores de eventos são implementados por classes Java frequentemente chamadas de “backing beans” ou, mais simplesmente, beans.
Agora, vamos esclarecer a relação entre a arquitetura web MVC e a arquitetura em camadas. São dois conceitos diferentes que, às vezes, são confundidos. Consideremos uma aplicação web JSF de camada única:
![]() |
Se implementarmos a camada [web] com JSF, teremos, de fato, uma arquitetura web MVC, mas não uma arquitetura multicamadas. Nesse caso, a camada [web] cuidará de tudo: apresentação, lógica de negócios e acesso aos dados. Com a JSF, serão os beans que realizarão esse trabalho.
Agora, vamos considerar uma arquitetura web multicamadas:
![]() |
A camada [web] pode ser implementada sem framework e sem seguir o modelo MVC. Temos, então, uma arquitetura multicamadas, mas a camada web não implementa o modelo MVC.
Em MVC, afirmamos que o modelo M era o da vista V, c.a.d, ou seja, o conjunto de dados exibidos pela vista V. Frequentemente, é apresentada outra definição do modelo M de MVC:
![]() |
Muitos autores consideram que o que está à direita da camada [web] forma o modelo M do MVC. Para evitar ambiguidades, falaremos:
- modelo do domínio ao nos referirmos a tudo o que está à direita da camada [web],
- de modelo da visualização quando nos referirmos aos dados exibidos por uma visualização V.
Daqui em diante, o termo “modelo M” se referirá exclusivamente ao modelo de uma vista V.
2.3. Exemplo mv-jsf2-01: os elementos de um projeto JSF
Os primeiros exemplos se limitarão à única camada web implementada com JSF 2:
![]() |
Quando os conceitos básicos forem assimilados, estudaremos exemplos mais complexos com arquiteturas multicamadas.
2.3.1. Geração do projeto
Geramos nosso primeiro projeto JSF2 com o NetBeans 7.
![]() |
- no [1], criamos um novo projeto,
- em [2], selecione a categoria [Maven] e o tipo de projeto [Web Application],
![]() |
- em [3], indicar a pasta pai da pasta do novo projeto,
- em [4], nomeie o projeto,
- em [5], escolher um servidor. No NetBeans 7, é possível escolher entre os servidores Apache Tomcat e GlassFish. A diferença entre os dois é que o GlassFish suporta EJB (Enterprise Java Bean), enquanto o Tomcat não. Em nossos exemplos, não vamos usar JSF. Portanto, aqui podemos escolher qualquer servidor,
- no [6], escolhemos a versão Java EE 6 Web,
- em [7], o projeto gerado.
Vamos examinar os elementos do projeto e explicar a função de cada um.
![]() |
- em [1]: os diferentes ramos do projeto:
- [Web Pages]: conterá as páginas da web (.xhtml, .jsp, .html), os recursos (imagens, documentos diversos), a configuração da camada web, bem como a do framework JSF;
- [Source packages]: as classes Java do projeto;
- [Dependencies]: os arquivos .jar necessários ao projeto e gerenciados pelo framework Maven;
- [Java Dependencies]: os arquivos .jar necessários ao projeto e não gerenciados pelo framework Maven;
- [Project Files]: arquivo de configuração do Maven e do NetBeans,
![]() |
- em [2]: o ramo [Web Pages],
Ela contém a seguinte página [index.jsp]:
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/HTML4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>JSP Page</title>
</head>
<body>
<h1>Hello World!</h1>
</body>
</html>
Trata-se de uma página da web que exibe a sequência de caracteres 'Hello World' em letras grandes.
O arquivo [META-INF/context.xml] é o seguinte:
<?xml version="1.0" encoding="UTF-8"?>
<Context antiJARLocking="true" path="/mv-jsf2-01"/>
A linha 2 indica que o contexto da aplicação (ou seu nome) é /mv-jsf2-01. Isso significa que as páginas da web do projeto serão solicitadas por meio de um URL no formato http://machine:port/mv-jsf2-01/page. O contexto é, por padrão, o nome do projeto. Não precisaremos alterar esse arquivo.
![]() |
- em [3], o ramo [Source Packages],
Este ramo contém os códigos-fonte das classes Java do projeto. Aqui não há nenhuma classe. O NetBeans gerou um pacote padrão que pode ser excluído: [4].
![]() |
- em [5], o ramo [Dependencies],
Este ramo exibe todas as bibliotecas necessárias ao projeto e gerenciadas pelo Maven. Todas as bibliotecas listadas aqui serão baixadas automaticamente pelo Maven. É por isso que um projeto Maven precisa de acesso à Internet. As bibliotecas baixadas serão armazenadas localmente. Se outro projeto precisar de uma biblioteca já presente localmente, ela não será baixada. Veremos que essa lista de bibliotecas, bem como os repositórios onde elas podem ser encontradas, são definidos no arquivo de configuração do projeto Maven.
![]() |
- em [6], as bibliotecas necessárias ao projeto e não gerenciadas pelo Maven,
![]() |
- em [7], os arquivos de configuração do projeto Maven:
- [nb-configuration.xml] é o arquivo de configuração do NetBeans. Não vamos nos deter nele.
- [pom.xml]: o arquivo de configuração do Maven. POM significa Project Object Model. Às vezes, será necessário intervir diretamente nesse arquivo.
O arquivo [pom.xml] gerado é o seguinte:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>istia.st</groupId>
<artifactId>mv-jsf2-01</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<name>mv-jsf2-01</name>
<properties>
<endorsed.dir>${project.build.directory}/endorsed</endorsed.dir>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-web-api</artifactId>
<version>6.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
<compilerArguments>
<endorseddirs>${endorsed.dir}</endorseddirs>
</compilerArguments>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.1.1</version>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.1</version>
<executions>
<execution>
<phase>validate</phase>
<goals>
<goal>copy</goal>
</goals>
<configuration>
<outputDirectory>${endorsed.dir}</outputDirectory>
<silent>true</silent>
<artifactItems>
<artifactItem>
<groupId>javax</groupId>
<artifactId>javaee-endorsed-api</artifactId>
<version>6.0</version>
<type>jar</type>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
- as linhas 5 a 8 definem o objeto (artefato) Java que será criado pelo projeto Maven. Essas informações provêm do assistente utilizado durante a criação do projeto:
![]() |
Um objeto Maven é definido por quatro propriedades:
- [groupId]: uma informação que se assemelha a um nome de pacote. Assim, as bibliotecas do framework Spring têm groupId=org.springframework; as do framework JSF têm groupId=javax.faces,
- [artifactId]: o nome do objeto Maven. No grupo [org.springframework], encontramos, portanto, os seguintes artifactId: spring-context, spring-core, spring-beans, ... No grupo [javax.faces], encontramos o artifactId e o jsf-api,
- [version]: número da versão do artefato Maven. Assim, o artefato org.springframework.spring-core possui as seguintes versões: 2.5.4, 2.5.5, 2.5.6, 2.5.6.SECO1, ...
- [packaging]: o formato do artefato, geralmente war ou jar.
Nosso projeto Maven irá, portanto, gerar um [war] (linha 8) no grupo [istia.st] (linha 5), denominado [mv-jsf2-01] (linha 6) e com a versão [1.0-SNAPSHOT] (linha 7). Essas quatro informações devem definir de forma única um artefato Maven.
As linhas 17 a 24 listam as dependências do projeto Maven, ou seja, a lista das bibliotecas necessárias ao projeto. Cada biblioteca é definida pelas quatro informações (groupId, artifactId, versão, packaging). Quando a informação packaging está ausente, como neste caso, o arquivo JAR packaging é utilizado. Acrescenta-se outra informação, scope, que determina em quais momentos do ciclo de vida do projeto a biblioteca é necessária. O valor padrão é compile, o que indica que a biblioteca é necessária tanto para a compilação quanto para a execução. O valor provided significa que a biblioteca é necessária durante a compilação, mas não durante a execução. Aqui, durante a execução, ela será fornecida pelo servidor Tomcat 7.
2.3.2. Execução do projeto
Executamos o projeto:
![]() |
Em [1], o projeto Maven é executado. O servidor Tomcat é então iniciado, caso ainda não estivesse em execução. Um navegador também é aberto e a página URL do contexto do projeto é solicitada como [2]. Como nenhum documento é solicitado, a página index.html, index.jsp, index.xhtml é então utilizada, caso exista. Neste caso, será a página [index.jsp].
2.3.3. O sistema de arquivos de um projeto Maven
![]() |
- [1]: o sistema de arquivos do projeto está na aba [Files],
- [2]: os códigos-fonte Java estão na pasta [src / main / java],
- [3]: as páginas da web estão na pasta [src / main / webapp],
- [4]: a pasta [target] é criada durante a compilação do projeto,
- [5]: neste caso, a compilação do projeto criou um arquivo [mv-jsf2-01-1.0-SNAPSHOT.war]. Foi esse arquivo que foi executado pelo servidor Tomcat.
2.3.4. Configurar um projeto para JSF
Nosso projeto atual não é um projeto JSF. Faltam nele as bibliotecas do framework JSF. Para transformar o projeto atual em um projeto JSF, proceda da seguinte maneira:
![]() |
- em [1], acesse as propriedades do projeto,
- no [2], seleciona-se a categoria [Frameworks],
- em [3], adiciona-se um framework,
![]() |
- No [4], escolhe-se o Java Server Faces,
- em [5], o NetBeans sugere a versão 2.1 do framework. Aceitamos,
- em [6], o projeto passa a incluir novas dependências.
O arquivo [pom.xml] foi atualizado para refletir essa nova configuração:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>istia.st</groupId>
<artifactId>mv-jsf2-01</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<name>mv-jsf2-01</name>
...
<dependencies>
<dependency>
<groupId>com.sun.faces</groupId>
<artifactId>jsf-api</artifactId>
<version>2.1.1-b04</version>
</dependency>
<dependency>
<groupId>com.sun.faces</groupId>
<artifactId>jsf-impl</artifactId>
<version>2.1.1-b04</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.1.2</version>
</dependency>
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-web-api</artifactId>
<version>6.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
...
</build>
<repositories>
<repository>
<URL>http://download.java.net/maven/2/</URL>
<id>jsf20</id>
<layout>default</layout>
<name>Repository for library Library[jsf20]</name>
</repository>
<repository>
<URL>http://repo1.maven.org/maven2/</URL>
<id>jstl11</id>
<layout>default</layout>
<name>Repository for library Library[jstl11]</name>
</repository>
</repositories>
</project>
Nas linhas 14 a 33, novas dependências foram adicionadas. O Maven as baixa automaticamente. Ele as busca no que chamamos de repositórios. O repositório central (Central Repository) é utilizado automaticamente. É possível adicionar outros repositórios por meio da tag <repository>. Aqui, dois repositórios foram adicionados:
- linhas 46-51: um repositório para a biblioteca JSF 2,
- linhas 52-57: um repositório para a biblioteca JSTL 1.1.
O projeto também ganhou uma nova página da web:
![]() |
A página [index.HTML] é a seguinte:
<?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://java.sun.com/jsf/html">
<h:head>
<title>Facelet Title</title>
</h:head>
<h:body>
Hello from Facelets
</h:body>
</html>
Temos aqui um arquivo XML (linha 1). Nele encontramos as tags do HTML, mas no formato XML. Isso é chamado de XHTML. A tecnologia utilizada para criar páginas da web com o JSF 2 é chamada de Facelets. Por isso, às vezes, a página XHTML é chamada de página Facelet.
As linhas 3 e 4 definem a tag <html> com o namespace XML (xmlns=XML Name Space).
- A linha 3 define o namespace principal http://www.w3.org/1999/xhtml,
- a linha 4 define o espaço de nomes http://java.sun.com/jsf/html das tags HTML. Essas tags serão prefixadas por h:, conforme indicado por xmlns:h. Essas tags podem ser encontradas nas linhas 5, 7, 8 e 10.
Ao encontrar a declaração de um espaço de nomes, o servidor web irá explorar as pastas [META-INF] e Classpath do aplicativo, em busca de arquivos com a extensão .tld (TagLib Definition). Nesse caso, ele os encontrará no arquivo [jsf-impl.jar] e [1,2]:
![]() |
Vamos examinar o arquivo [3] e o arquivo [HTML_basic.tld]:
- na linha 19, a URI da biblioteca de tags,
- na linha 16, seu nome abreviado.
As definições das diferentes tags <h:xx> estão contidas neste arquivo. Essas tags são gerenciadas por classes Java que também se encontram no artefato [jsf-impl.jar].
Voltemos ao nosso projeto JSF. Ele foi ampliado com um novo ramo:
![]() |
O ramo [Other Sources] [1] contém os arquivos que devem estar no Classpath do projeto e que não são código Java. Esse é o caso dos arquivos de mensagens em JSF. Vimos que, sem a inclusão do framework JSF no projeto, esse ramo não existe. Para criá-la, basta criar a pasta [src / main / resources] [3] na aba [Files] [2].
Por fim, uma nova pasta apareceu no ramo [Web Pages]:
![]() |
A pasta [WEB-INF] foi criada contendo o arquivo [web.xml] . Este arquivo configura o aplicativo web:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<context-param>
<param-name>javax.faces.PROJECT_STAGE</param-name>
<param-value>Development</param-value>
</context-param>
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<URL-pattern>/faces/*</URL-pattern>
</servlet-mapping>
<session-config>
<session-timeout>
30
</session-timeout>
</session-config>
<welcome-file-list>
<welcome-file>faces/index.xhtml</welcome-file>
</welcome-file-list>
</web-app>
- as linhas 7 a 10 definem um servlet, c.a.d, uma classe Java capaz de processar as solicitações dos clientes. Uma aplicação JSF funciona da seguinte maneira:
![]() |
Essa arquitetura implementa o Padrão de Design MVC (Modelo, Visão, Controlador). Relembramos o que já foi escrito acima. O processamento de uma solicitação de um cliente ocorre de acordo com as quatro etapas a seguir:
1 - solicitação - o navegador do cliente envia uma solicitação ao controlador [Faces Servlet]. Este recebe todas as solicitações dos clientes. É a porta de entrada do aplicativo. É o C de MVC,
2 - processamento – o controlador C processa essa solicitação. Para isso, ele conta com a ajuda de gerenciadores de eventos específicos da aplicação [2a]. Esses gerenciadores podem precisar da ajuda da camada de negócios [2b]. Uma vez processada a solicitação do cliente, ela pode gerar diversas respostas. Um exemplo clássico é:
- uma página de erros, caso a solicitação não tenha podido ser processada corretamente;
- uma página de confirmação, caso contrário,
3 – navegação – o controlador escolhe a resposta (= vista) a ser enviada ao cliente. Escolher a resposta a ser enviada ao cliente envolve várias etapas:
- escolher o Facelet que irá gerar a resposta. É o que chamamos de vista V, o V de MVC. Essa escolha depende, em geral, do resultado da execução da ação solicitada pelo usuário;
- fornecer a esse Facelet os dados de que ele precisa para gerar essa resposta. De fato, ela contém, na maioria das vezes, informações calculadas pelo controlador. Essas informações formam o que chamamos de modelo M da visualização, o M de MVC,
A etapa 3 consiste, portanto, na escolha de uma visualização V e na construção do modelo M necessário para ela.
4 - resposta - o controlador C solicita que a Facelet escolhida seja exibida. Esta utiliza o modelo M preparado pelo controlador C para inicializar as partes dinâmicas da resposta que deve enviar ao cliente. A forma exata dessa resposta pode variar: pode ser um fluxo HTML, PDF, Excel, ...
Em um projeto JSF:
- o controlador C é o servlet [javax.faces.webapp.FacesServlet],
- as visualizações V são implementadas por páginas que utilizam a tecnologia Facelets,
- os modelos M e os gerenciadores de eventos são implementados por classes Java frequentemente chamadas de “backing beans” ou, mais simplesmente, Beans.
Voltemos ao conteúdo do arquivo [web.xml):
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<context-param>
<param-name>javax.faces.PROJECT_STAGE</param-name>
<param-value>Development</param-value>
</context-param>
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<URL-pattern>/faces/*</URL-pattern>
</servlet-mapping>
<session-config>
<session-timeout>
30
</session-timeout>
</session-config>
<welcome-file-list>
<welcome-file>faces/index.xhtml</welcome-file>
</welcome-file-list>
</web-app>
- linhas 12-15: a tag <servlet-mapping> serve para associar um servlet a um URL solicitado pelo navegador do cliente. Aqui, está indicado que os URL do formato [/faces/*] devem ser processados pelo servlet de nome [Faces Servlet]. Este está definido nas linhas 7 a 10. Como não há outra tag <servlet-mapping> no arquivo, isso significa que o servlet [Faces Servlet] processará apenas os URL do formato [/faces/*]. Vimos que o contexto da aplicação se chamava [/mv-jsf2-01]. Os URL dos clientes processados pelo servlet [Faces Servlet] terão, portanto, o formato [http://machine:port/mv-jsf2-01/faces/*]. As páginas .html e .jsp serão processadas, por padrão, pelo próprio contêiner de servlets, e não por uma servlet específica. De fato, o contêiner de servlets sabe como gerenciá-las,
- linhas 7-10: definem o servlet [Faces Servlet]. Como todas as solicitações URL aceitas são direcionadas para ele, ele é o controlador C do modelo MVC,
- linha 10: indica que a servlet deve ser carregada na memória assim que o servidor web for iniciado. Por padrão, uma servlet só é carregada ao receber a primeira solicitação que lhe é feita,
- linhas 3-6: definem um parâmetro destinado ao servlet [Faces Servlet]. O parâmetro javax.faces.PROJECT_STAGE define a fase em que o projeto executado se encontra. Na fase de Desenvolvimento, a servlet [Faces Servlet] exibe mensagens de erro úteis para a depuração. Na fase de Produção, essas mensagens não são mais exibidas,
- linhas 17-19: duração, em minutos, de uma sessão. Um cliente interage com a aplicação por meio de uma sequência de ciclos de solicitação/resposta. Cada ciclo utiliza uma conexão TCP-IP própria, criada a cada novo ciclo. Portanto, se um cliente C fizer duas solicitações D1 e D2, o servidor S não tem como saber que as duas solicitações pertencem ao mesmo cliente C. O servidor S não mantém o histórico do cliente. É o protocolo HTTP utilizado (Protocolo de Transporte HyperText) que determina isso: o cliente se comunica com o servidor por meio de uma sucessão de ciclos de solicitação do cliente/resposta do servidor, utilizando a cada vez uma nova conexão TCP-IP. Trata-se de um protocolo sem estado. Em outros protocolos, como, por exemplo, o FTP (File Transfer Protocol), o cliente C utiliza a mesma conexão durante todo o tempo de sua interação com o servidor S. Uma conexão está, portanto, vinculada a um cliente específico. O servidor S sempre sabe com quem está lidando. Para poder reconhecer que uma solicitação pertence a um determinado cliente, o servidor web pode utilizar a técnica de sessão:
- na primeira solicitação de um cliente, o servidor S envia a resposta esperada, juntamente com um token, uma sequência aleatória de caracteres exclusiva para esse cliente;
- em cada solicitação subsequente, o cliente C reenvia ao servidor S o token que recebeu, permitindo assim que o servidor S o reconheça.
A aplicação agora tem a possibilidade de solicitar ao servidor que armazene informações associadas a um determinado cliente. Isso é conhecido como sessão do cliente. A linha 18 indica que a duração de uma sessão é de 30 minutos. Isso significa que, se um cliente C não fizer uma nova solicitação por 30 minutos, sua sessão será encerrada e as informações nela contidas serão perdidas. Na próxima solicitação, tudo ocorrerá como se ele fosse um novo cliente e uma nova sessão será iniciada,
- linhas 21-23: a lista de páginas a serem exibidas quando o usuário solicita o contexto sem especificar uma página, por exemplo, aqui [http://machine:port/mv-jsf2-01]. Nesse caso, o servidor web (não o servlet) verifica se o aplicativo definiu uma tag <welcome-file-list>. Se sim, ele exibe a primeira página encontrada na lista. Se ela não existir, a segunda página, e assim por diante até encontrar uma página existente. Nesse caso, quando o cliente solicita o URL [http://machine:port/mv-jsf2-01], é o URL [http://machine:port/mv-jsf2-01/index.xhtml] que será exibido.
2.3.5. Executar o projeto
Ao executar o novo projeto, o resultado obtido no navegador é o seguinte:
![]() |
- em [1], o contexto foi solicitado sem especificação de documento,
- em [2], conforme explicado, é a página inicial (welcome-file) [index.xhtml] que é exibida.
Pode-se ter a curiosidade de examinar o código-fonte recebido [3]:
Recebemos o HTML. Todas as tags <h:xx> do index.xhtml foram traduzidas para suas correspondentes no HTML.
2.3.6. O repositório local do Maven
Já mencionamos que o Maven baixa as dependências necessárias para o projeto e as armazena localmente. É possível explorar esse repositório local:
![]() |
- em [1], seleciona-se a opção [Window / Other / Maven Repository Browser],
- em [2], abre-se uma aba [Maven Repositories],
- em [3], ele contém dois ramos: um para o repositório local e outro para o repositório central. Este último é gigantesco. Para visualizar seu conteúdo, é preciso atualizar seu índice [4]. Essa atualização leva várias dezenas de minutos.
![]() |
- em [5], as bibliotecas do repositório local,
- em [6], há um ramo [istia.st] que corresponde ao [groupId] do nosso projeto,
- Em [7], é possível acessar as propriedades do repositório local;
- em [8], temos o caminho do repositório local. É útil conhecê-lo, pois, às vezes (raramente), o Maven deixa de usar a versão mais recente do projeto. Fazemos alterações e percebemos que elas não são incorporadas. Nesse caso, podemos excluir manualmente o ramo do repositório local correspondente ao nosso [groupId]. Isso força o Maven a recriar o ramo a partir da versão mais recente do projeto.
2.3.7. Pesquisar um artefato com o Maven
Vamos aprender agora a procurar um artefato com o Maven. Comecemos pela lista de dependências atuais do arquivo [pom.xml]:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>istia.st</groupId>
<artifactId>mv-jsf2-01</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<name>mv-jsf2-01</name>
...
<dependencies>
<dependency>
<groupId>com.sun.faces</groupId>
<artifactId>jsf-api</artifactId>
<version>2.1.1-b04</version>
</dependency>
<dependency>
<groupId>com.sun.faces</groupId>
<artifactId>jsf-impl</artifactId>
<version>2.1.1-b04</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.1.2</version>
</dependency>
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-web-api</artifactId>
<version>6.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
...
</build>
<repositories>
<repository>
<url>http://download.java.net/maven/2/</url>
<id>jsf20</id>
<layout>default</layout>
<name>Repository for library Library[jsf20]</name>
</repository>
<repository>
<url>http://repo1.maven.org/maven2/</url>
<id>jstl11</id>
<layout>default</layout>
<name>Repository for library Library[jstl11]</name>
</repository>
</repositories>
</project>
As linhas 13 a 40 definem as dependências e as linhas 45 a 58 indicam os repositórios onde elas podem ser encontradas, além do repositório central, que é sempre utilizado. Vamos modificar as dependências para usar as bibliotecas em suas versões mais recentes.
![]() |
Primeiramente, removemos as dependências atuais [1]. O arquivo [pom.xml] é então modificado:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
...
<dependencies>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-web-api</artifactId>
<version>6.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
...
<repositories>
<repository>
<url>http://download.java.net/maven/2/</url>
<id>jsf20</id>
<layout>default</layout>
<name>Repository for library Library[jsf20]</name>
</repository>
<repository>
<url>http://repo1.maven.org/maven2/</url>
<id>jstl11</id>
<layout>default</layout>
<name>Repository for library Library[jstl11]</name>
</repository>
</repositories>
</project>
Linhas 5-12: as dependências removidas não aparecem mais no [pom.xml]. Agora, vamos procurá-las nos repositórios do Maven.
![]() |
- no [1], adiciona-se uma dependência ao projeto;
- no [2], é necessário especificar informações sobre o artefato procurado (groupId, artifactId, versão, empacotamento (Tipo) e escopo). Começamos especificando o [groupId] [3],
- em [4], digitamos [espace] para exibir a lista de artefatos possíveis. Aqui, [jsf-api] e [jsf-impl]. Escolhemos [jsf-api],
- em [5], seguindo o mesmo procedimento, escolhemos a versão mais recente. O tipo de pacote é jar.
Procedemos dessa forma para todos os artefatos:
![]() | ![]() | ![]() | ![]() |
![]() |
No arquivo [6], as dependências adicionadas aparecem no projeto. O arquivo [pom.xml] reflete essas alterações:
<dependencies>
<dependency>
<groupId>com.sun.faces</groupId>
<artifactId>jsf-api</artifactId>
<version>2.1.7</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>com.sun.faces</groupId>
<artifactId>jsf-impl</artifactId>
<version>2.1.7</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-web-api</artifactId>
<version>6.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
Suponhamos agora que não se conheça o [groupId] do artefato desejado. Por exemplo, queremos usar o Hibernate como ORM (Mapeador Objeto-Relacional) e isso é tudo o que sabemos. Nesse caso, podemos acessar o site [http://mvnrepository.com/]:
![]() |
No [1], é possível digitar palavras-chave. Digite hibernate e inicie a pesquisa.
![]() |
- no [2], vamos escolher o [groupId], o org.hibernate e o [artifactId], o hibernate-core,
- em [3], vamos escolher a versão 4.1.2-Final,
- em [4], obtemos o código Maven para colar no arquivo [pom.xml]. Fazemos isso.
<dependencies>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>4.1.2.Final</version>
</dependency>
<dependency>
<groupId>com.sun.faces</groupId>
<artifactId>jsf-api</artifactId>
<version>2.1.7</version>
<type>jar</type>
</dependency>
...
</dependencies>
Salvamos o arquivo [pom.xml]. O Maven então inicia o download das novas dependências. O projeto evolui da seguinte forma:
![]() |
- em [5], a dependência [hibernate-core-4.1.2-Final]. No repositório onde foi encontrado, esse [artifactId] também é descrito por um arquivo [pom.xml]. Esse arquivo foi lido e o Maven descobriu que o [artifactId] tinha dependências. Ele também as baixa. Ele fará isso para cada [artifactId] baixado. No final, encontramos no [6] dependências que não havíamos solicitado diretamente. Elas são indicadas por um ícone diferente daquele do [artifactId] principal.
Neste documento, utilizamos o Maven principalmente por causa dessa característica. Isso nos poupa de precisar conhecer todas as dependências de uma biblioteca que queremos usar. Deixamos que o Maven as gerencie. Além disso, ao compartilhar um arquivo [pom.xml] entre desenvolvedores, temos a garantia de que cada desenvolvedor está usando as mesmas bibliotecas.
Nos exemplos a seguir, nos limitaremos a fornecer o arquivo [pom.xml] utilizado. O leitor só precisará usá-lo para se encontrar nas mesmas condições do documento. Além disso, os projetos Maven são reconhecidos pelos principais ambientes de desenvolvimento Java (Eclipse, NetBeans, IntelliJ, JDeveloper). Assim, o leitor poderá usar seu ambiente favorito para testar os exemplos.
2.4. Exemplo mv-jsf2-02: gerenciador de eventos – internacionalização – navegação entre páginas
2.4.1. A aplicação
A aplicação é a seguinte:
![]() |
- em [1], a página inicial da aplicação,
- em [2], dois links para alterar o idioma das páginas da aplicação,
- em [3], um link de navegação para outra página,
- ao clicar em [3], a página [4] é exibida,
- o link [5] permite voltar à página inicial.
![]() |
- na página inicial [1], os links [2] permitem alterar o idioma,
- em [3], a página inicial em inglês.
2.4.2. O projeto NetBeans
Será gerado um novo projeto web conforme explicado no parágrafo 2.3.1. Ele será denominado mv-jsf2-02:
![]() |
- em [1], o projeto gerado,
- em [2], excluímos o pacote [istia.st.mvjsf202] e o arquivo [index.jsp],
- No arquivo [3], foram adicionadas dependências do Maven por meio do seguinte arquivo [pom.xml]:
<dependencies>
<dependency>
<groupId>com.sun.faces</groupId>
<artifactId>jsf-api</artifactId>
<version>2.1.7</version>
</dependency>
<dependency>
<groupId>com.sun.faces</groupId>
<artifactId>jsf-impl</artifactId>
<version>2.1.7</version>
</dependency>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-web-api</artifactId>
<version>6.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
As dependências adicionadas são as do framework JSF. Basta copiar as linhas acima para o arquivo [pom.xml], substituindo as dependências antigas.
![]() |
- em [4, 5]: cria-se uma pasta [src / main / resources] na aba [Files],
- em [6], na aba [Projects], o que criou o ramo [Other Sources].
Agora temos um projeto JSF. Nele, criaremos diferentes tipos de arquivos:
- páginas da web no formato XHTML,
- classes Java,
- arquivos de mensagens,
- o arquivo de configuração do projeto JSF.
Vamos ver como criar cada tipo de arquivo:
![]() |
- no [1], criamos uma página JSF
- no [2], criamos uma página [index.xhtml] no formato [Facelets] [3],
- em [4], foram criados dois arquivos: [index.xhtml] e [WEB-INF / web.xml].
O arquivo [web.xml] configura o aplicativo JSF. É o seguinte:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<context-param>
<param-name>javax.faces.PROJECT_STAGE</param-name>
<param-value>Development</param-value>
</context-param>
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<URL-pattern>/faces/*</URL-pattern>
</servlet-mapping>
<session-config>
<session-timeout>
30
</session-timeout>
</session-config>
<welcome-file-list>
<welcome-file>faces/index.xhtml</welcome-file>
</welcome-file-list>
</web-app>
Já comentamos esse arquivo no parágrafo 2.3.4. Vamos relembrar suas principais propriedades:
- todos os URL do tipo faces/* são processados pelo servlet [javax.faces.webapp.FacesServlet],
- a página [index.xhtml] é a página inicial do aplicativo.
O arquivo [index.xhtml] criado é o seguinte:
<?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://java.sun.com/jsf/html">
<h:head>
<title>Facelet Title</title>
</h:head>
<h:body>
Hello from Facelets
</h:body>
</html>
Já nos deparamos com esse arquivo no parágrafo 2.3.4.
Vamos agora criar uma classe Java:
![]() |
- em [1], criamos uma classe Java no ramo [Source Packages],
- em [2], atribuímos um nome a ela e a colocamos no pacote [3],
- em [4], a classe criada aparece no projeto.
O código da classe criada é um esboço de classe:
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package istia.st;
/**
*
* @author Serge Tahé
*/
public class Form {
}
Por fim, vamos criar um arquivo de mensagens:
- em [1], criação de um arquivo [Properties],
- em [2], definimos o nome do arquivo e, em [3], sua pasta,
- em [4], o arquivo [messages.properties] foi criado.
Às vezes, é necessário criar o arquivo [WEB-INF/faces-config.xml] para configurar o projeto JSF. Esse arquivo era obrigatório com o JSF 1. Ele é opcional com o JSF 2. No entanto, é necessário se o site JSF for internacionalizado. Esse será o caso posteriormente. Portanto, mostraremos agora como criar esse arquivo de configuração.
![]() |
- no [1], criamos o arquivo de configuração JSF,
- em [2], definimos seu nome e, em [3], sua pasta,
- em [4], o arquivo criado.
O arquivo [faces-config.xml] criado é o seguinte:
<?xml version='1.0' encoding='UTF-8'?>
<!-- =========== FULL CONFIGURATION FILE ================================== -->
<faces-config version="2.0"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd">
</faces-config>
A tag raiz é <faces-config>. O corpo dessa tag está vazio. Teremos que preenchê-lo.
Agora temos todos os elementos para criar um projeto JSF. Nos exemplos a seguir, apresentaremos o projeto JSF completo e, em seguida, detalharemos seus elementos um por um. Apresentamos agora um projeto para explicar os conceitos:
- gerenciador de eventos de um formulário,
- de internacionalização das páginas de um site JSF,
- de navegação entre páginas.
O projeto [mv-jsf2-02] fica da seguinte forma. O leitor pode encontrá-lo no site de exemplos (ver parágrafo 1.2).
![]() |
- em [1], dos arquivos de configuração do projeto JSF,
- em [2], as páginas JSF do projeto,
- em [3], a única classe Java,
- em [4], os arquivos de mensagens.
2.4.3. A página [index.xhtml]
O arquivo [index.xhtml] [1] envia a página [2] para o navegador do cliente:
![]() |
O código que gera essa página é o seguinte:
<?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://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<f:view locale="#{changeLocale.locale}">
<head>
...
</head>
<body>
....
</body>
</f:view>
</html>
- linhas 7-9: os espaços de nomes/bibliotecas de tags utilizadas pela página. As tags com o prefixo h são tags HTML, enquanto as tags com o prefixo f são específicas do JSF,
- linha 10: a tag <f:view> serve para delimitar o código que o mecanismo JSF deve processar, aquele em que aparecem as tags <f:xx>. O atributo locale permite especificar um idioma de exibição para a página. Aqui, usaremos dois: o inglês e o francês. O valor do atributo `locale` é expresso na forma de uma expressão EL (Expression Language) #{expressão}. A forma da expressão pode variar. Na maioria das vezes, vamos expressá-la na forma `bean['clé']` ou `bean.champ`. Em nossos exemplos, bean será uma classe Java ou um arquivo de mensagens. Com JSF 1, esses beans precisavam ser declarados no arquivo [faces-config.xml]. Com JSF 2, isso não é mais obrigatório para as classes Java. Agora é possível usar anotações que transformam uma classe Java em um bean reconhecido pelo JSF 2. O arquivo de mensagens, por sua vez, deve ser declarado no arquivo de configuração [faces-config.xml].
2.4.4. O bean [changeLocale]
Na expressão EL #{changeLocale.locale}:
- changeLocale é o nome de um bean, neste caso a classe Java ChangeLocale,
- locale é um campo da classe ChangeLocale. A expressão é avaliada por [ChangeLocale].getLocale(). De maneira geral, a expressão #{bean.champ} é avaliada como [Bean].getChamp(), onde [Bean] é uma instância da classe Java à qual foram atribuídos os nomes bean e getChamp, o getter associado ao campo champ do bean.
A classe ChangeLocale é a seguinte:
package utils;
import java.io.Serializable;
import javax.faces.bean.ManagedBean;
import javax.enterprise.context.SessionScoped;
@ManagedBean
@SessionScoped
public class ChangeLocale implements Serializable{
// a localização das páginas
private String locale="fr";
public ChangeLocale() {
}
...
public String getLocale() {
return locale;
}
}
- linha 11: o campo `locale`,
- linha 17: seu getter,
- linha 7: a anotação ManagedBean torna a classe Java ChangeLocale um bean reconhecido por JSF. Um bean é identificado por um nome. Esse nome pode ser definido pelo atributo name da anotação: @ManagedBean(name= "xx "). Na ausência do atributo name, utiliza-se o nome da classe, transformando seu primeiro caractere em minúscula. O nome do bean ChangeLocale é, portanto, changeLocale. É importante observar que a anotação ManagedBean pertence ao pacote javax.faces.bean.ManagedBean e não ao pacote javax.annotations.ManagedBean.
- linha 8: a anotação SessionScoped define o escopo do bean. Existem vários. Utilizaremos com frequência os três seguintes:
- RequestScoped: o tempo de vida do bean corresponde ao ciclo de solicitação do navegador/resposta do servidor. Se, para processar uma nova solicitação do mesmo navegador ou de outro, esse bean for necessário novamente, ele será instanciado novamente,
- SessionScoped: o tempo de vida do bean corresponde ao da sessão de um cliente específico. O bean é criado inicialmente para atender a uma das solicitações desse cliente. Em seguida, permanecerá na memória durante a sessão desse cliente. Esse tipo de bean geralmente armazena dados específicos de um determinado cliente. Ele será destruído quando a sessão do cliente for encerrada,
- ApplicationScoped: a duração do bean é igual à da própria aplicação. Um bean com essa duração é, na maioria das vezes, compartilhado por todos os clientes da aplicação. Ele é geralmente inicializado no início da aplicação.
Essas anotações existem em dois pacotes: javax.enterprise.context.SessionScoped (JSF 2) e javax.faces.bean.SessionScoped (JSF 1). Aqui, utilizamos o pacote JSF 2. Isso nos obriga a criar o arquivo [WEB-INF / beans.xml]:
![]() |
Esse arquivo é gerado automaticamente pelo NetBeans ao importar o pacote [javax.enterprise.context.SessionScoped]. Seu conteúdo é o seguinte:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
</beans>
Fora da tag raiz <beans>, o arquivo está vazio. Isso é suficiente. Basta que ele exista.
Por fim, vale ressaltar que a classe [ChangeLocale] implementa a interface [Serializable]. Isso é obrigatório para os beans com escopo Session, que o servidor web pode precisar serializar em arquivos. Voltaremos a abordar o bean [ChangeLocale] posteriormente.
2.4.5. O arquivo de mensagens
Voltemos ao arquivo [index.xhtml]:
<?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://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<f:view locale="#{changeLocale.locale}">
<head>
<title><h:outputText value="#{msg['welcome.titre']}" /></title>
</head>
<body>
...
</body>
</f:view>
</html>
- linha 8: a tag <h:outputText> exibe o valor de uma expressão EL #{msg['welcome.titre']} na forma #{bean['champ']}. bean é o nome de uma classe Java ou de um arquivo de mensagens. Neste caso, trata-se de um arquivo de mensagens. Este deve ser declarado no arquivo de configuração [faces-config.xml]. O bean msg é declarado da seguinte forma:
<?xml version='1.0' encoding='UTF-8'?>
<!-- =========== FULL CONFIGURATION FILE ================================== -->
<faces-config version="2.0"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd">
<application>
<resource-bundle>
<base-name>
messages
</base-name>
<var>msg</var>
</resource-bundle>
</application>
</faces-config>
- linhas 11-18: a tag <application> serve para configurar a aplicação JSF,
- linhas 12-17: a tag <resource-bundle> serve para definir recursos para a aplicação, neste caso, um arquivo de mensagens,
- linhas 13-15: a tag <base-name> define o nome do arquivo de mensagens,
- linha 14: o arquivo se chamará messages[_CodeLangue][_CodePays].properties. A tag <base-name> define apenas a primeira parte do nome. O restante é implícito. Podem existir vários arquivos de mensagens, um por idioma:
![]() |
- em [1], vemos quatro arquivos de mensagens correspondentes ao nome base “messages” definido em [faces-config.xml],
- messages_fr.properties: contém as mensagens em francês (código fr);
- messages_en.properties: contém as mensagens em inglês (código en);
- messages_es_ES.properties: contém as mensagens em espanhol (código es) da Espanha (código ES). Existem outros tipos de espanhol, por exemplo, o da Bolívia (es_BO);
- messages.properties: é utilizado pelo servidor quando o idioma do computador no qual ele está sendo executado não possui nenhum arquivo de mensagens associado a ele. Seria utilizado, por exemplo, se o aplicativo fosse executado em um computador na Alemanha, onde o idioma padrão seria o alemão (de). Como não existe um arquivo [messages_de.properties], o aplicativo utilizaria o arquivo [messages.properties],
- em [2]: os códigos de idiomas seguem um padrão internacional,
- em [3]: o mesmo vale para os códigos dos países.
O nome do arquivo de mensagens é definido na linha 14. Ele será procurado no arquivo Classpath do projeto. Se estiver dentro de um pacote, este deve ser definido na linha 14, por exemplo, ressources.messages, se o arquivo [messages.properties] estiver na pasta [ressources] do Classpath. Como o nome, na linha 14, não contém nenhum pacote, o arquivo [messages.properties] deve ser colocado na raiz da pasta [src / main / resources]:
![]() |
No [1], na guia [Projects] do projeto NetBeans, o arquivo [messages.properties] é apresentado como uma lista das diferentes versões de mensagens definidas. As versões são identificadas por uma sequência de um a três códigos [codeLangue_codePays_codeVariante]. No [1], apenas o código [codeLangue] foi utilizado: en para o inglês, fr para o francês. Cada versão é objeto de um arquivo separado no sistema de arquivos.
No nosso exemplo, o arquivo de mensagens em francês [messages_fr.properties] conterá os seguintes elementos:
welcome.titre=Tutoriel JSF (JavaServer Faces)
welcome.langue1=Fran\u00e7ais
welcome.langue2=Anglais
welcome.page1=Page 1
page1.titre=page1
page1.entete=Page 1
page1.welcome=Page d'accueil
Já o arquivo [messages_en.properties] terá o seguinte conteúdo:
welcome.titre=JSF (JavaServer Faces) Tutorial
welcome.langue1=French
welcome.langue2=English
welcome.page1=Page 1
page1.titre=page1
page1.entete=Page 1
page1.welcome=Welcome page
O arquivo [messages.properties] é idêntico ao arquivo [messages_en.properties]. No final, o navegador do cliente poderá escolher entre páginas em francês e páginas em inglês.
Voltemos ao arquivo [faces-config.xml], que declara o arquivo de mensagens:
...
<application>
<resource-bundle>
<base-name>
messages
</base-name>
<var>msg</var>
</resource-bundle>
</application>
</faces-config>
A linha 8 indica que uma linha do arquivo de mensagens será referenciada pelo identificador msg nas páginas JSF. Esse identificador é utilizado no arquivo [index.xhtml] analisado:
<?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://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<f:view locale="#{changeLocale.locale}">
<head>
<title><h:outputText value="#{msg['welcome.titre']}" /></title>
</head>
<body>
...
</body>
</f:view>
</html>
A tag <h:outputText> da linha 8 exibirá o valor da mensagem (presença do identificador msg) da chave welcome.titre. Essa mensagem é procurada e encontrada no arquivo [messages.properties] do idioma ativo no momento. Por exemplo, para o francês:
welcome.titre=Tutoriel JSF (JavaServer Faces)
Uma mensagem tem o formato chave=valor. A linha 8 do arquivo [index.xhtml] fica da seguinte forma após a avaliação da expressão #{msg['welcome.titre']}:
<title><h:outputText value="Tutoriel JSF (JavaServer Faces)" /></title>
Esse mecanismo de arquivos de mensagens permite alterar facilmente o idioma das páginas de um projeto JSF. Fala-se em internacionalização do projeto ou, mais comumente, em sua abreviação i18n, porque a palavra “internacionalização” começa com i e termina com n, e há 18 letras entre o i e o n.
2.4.6. O formulário
Vamos continuar explorando o conteúdo do arquivo [index.xhtml]:
<?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://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<f:view locale="#{changeLocale.locale}">
<head>
<title><h:outputText value="#{msg['welcome.titre']}" /></title>
</head>
<body>
<h:form id="formulaire">
<h:panelGrid columns="2">
<h:commandLink value="#{msg['welcome.langue1']}" action="#{changeLocale.setFrenchLocale}"/>
<h:commandLink value="#{msg['welcome.langue2']}" action="#{changeLocale.setEnglishLocale}"/>
</h:panelGrid>
<h1><h:outputText value="#{msg['welcome.titre']}" /></h1>
<h:commandLink value="#{msg['welcome.page1']}" action="page1"/>
</h:form>
</body>
</f:view>
</html>
- linhas 11-18: a tag <h:form> introduz um formulário. Um formulário geralmente é composto por:
- tags de campos de entrada (texto, botões de opção, caixas de seleção, listas de itens, etc.);
- tags de validação do formulário (botões, links). É por meio de um botão ou link que o usuário envia suas entradas ao servidor, que as processará,
Qualquer tag JSF pode ser identificada por um atributo id. Na maioria das vezes, é possível dispensá-lo, e foi isso que foi feito na maioria das tags JSF utilizadas aqui. No entanto, esse atributo é útil em certos casos. Na linha 17, o formulário é identificado pelo id “formulário”. Neste exemplo, o id do formulário não será utilizado e poderia ter sido omitido.
- linhas 18-21: a tag <h:panelGrid> define aqui uma tabela HTML com duas colunas. Ela gera a tag HTML <table>,
- o formulário possui três links que acionam seu processamento, nas linhas 19, 20 e 23. A tag <h:commandLink> possui pelo menos dois atributos:
- value: o texto do link;
- action: ou uma sequência de caracteres C, ou a referência a um método que, após execução, retorna a sequência de caracteres C. Essa sequência de caracteres C pode ser:
- ou o nome de uma página JSF do projeto,
- ou um nome definido nas regras de navegação do arquivo [faces-config.xml] e associado a uma página JSF do projeto;
Em ambos os casos, a página JSF é exibida, assim que a ação definida pelo atributo action for executada.
Vamos examinar o funcionamento do processamento de formulários usando o exemplo do link da linha 13:
<h:commandLink value="#{msg['welcome.langue1']}" action="#{changeLocale.setFrenchLocale}"/>}"/>
Primeiramente, o arquivo de mensagens é utilizado para substituir a expressão #{msg['welcome.langue1']} por seu valor. Após a avaliação, a tag passa a ser:
<h:commandLink value="Français" action="#{changeLocale.setFrenchLocale}"/>}"/>
A tradução HTML dessa tag JSF será a seguinte:
<a href="<a href="view-source:http://localhost:8080/mv-jsf2-02/faces/page1.xhtml#">#</a>" onclick="mojarra.jsfcljs(document.getElementById('formulaire'),{'formulaire:j_idt8':'formulaire:j_idt8'},'');return false">Français</a>
o que resultará na seguinte aparência visual:
![]() |
Observe-se o atributo onclick da tag HTML <a>. Quando o usuário clicar no link [Français], o código JavaScript será executado. Esse código está incorporado na página recebida pelo navegador, e é o próprio navegador que o executa. O código JavaScript é amplamente utilizado nas tecnologias JSF e AJAX (Asynchronous JavaScript and XML). Em geral, seu objetivo é melhorar a usabilidade e a capacidade de resposta das aplicações web. Na maioria das vezes, ele é gerado automaticamente por ferramentas de software e, nesse caso, não é necessário compreendê-lo. Porém, às vezes, um desenvolvedor pode precisar adicionar código JavaScript às suas páginas JSF. Nesse caso, o conhecimento de JavaScript é necessário.
Não há necessidade, neste caso, de compreender o código JavaScript gerado para a tag JSF <h:commandLink>. No entanto, vale a pena observar dois pontos:
- o código JavaScript utiliza o identificador de formulário que atribuímos à tag JSF <h:form>,
- o JSF gera identificadores automáticos para todas as tags nas quais o atributo id não foi definido. Vemos um exemplo aqui: j_idt8. Atribuir um identificador claro às tags permite compreender melhor o código JavaScript gerado, caso seja necessário. Esse é o caso, principalmente, quando o desenvolvedor precisa adicionar ele mesmo código JavaScript que manipula os componentes da página. Nesse caso, ele precisa conhecer os identificadores id desses componentes.
O que acontecerá quando o usuário clicar no link [Français] da página acima? Consideremos a arquitetura de um aplicativo JSF:
![]() |
O controlador [Faces Servlet] receberá a solicitação do navegador do cliente no formato HTTP a seguir:
- linhas 1-2: o navegador solicita o URL [http://localhost:8080/mv-jsf2-02/faces/index.xhtml]. É sempre assim: os dados inseridos em um formulário JSF, obtido inicialmente com o URL URLFormulaire, são enviados para esse mesmo URL. O navegador dispõe de duas formas de enviar os valores inseridos: GET e POST. Com o método GET, os valores inseridos são enviados pelo navegador no URL que foi solicitado. No exemplo acima, o navegador poderia ter enviado a seguinte primeira linha:
GET /mv-jsf2-02/faces/index.xhtml?formulaire=formulaire&javax.faces.ViewState=-9139703055324497810%3A8197824608762605653&formulaire%3Aj_idt8=formulaire%3Aj_idt8 HTTP/1.1
Com o método POST utilizado aqui, o navegador envia ao servidor os valores inseridos por meio da linha 6.
- linha 3: indica o formato de codificação dos valores do formulário,
- linha 4: indica o tamanho em bytes da linha 6,
- linha 5: linha vazia que indica o fim dos cabeçalhos HTTP e o início dos 126 bytes dos valores do formulário,
- linha 6: os valores do formulário no formato element1=valor1&element2=valor2& ..., cujo formato de codificação é definido pela linha 3. Nesse formato de codificação, alguns caracteres são substituídos por seus valores hexadecimais. Esse é o caso do último elemento:
formulaire=formulaire&javax.faces.ViewState=...&formulaire%3Aj_idt8=formulaire%3Aj_idt8
onde %3A representa o caractere :. Portanto, é a sequência formulário:j_idt8=formulário:j_idt8 que é enviada ao servidor. Talvez nos lembremos de que já nos deparamos com o identificador j_idt8 quando analisamos o código HTML gerado para a tag
<h:commandLink value="#{msg['welcome.langue1']}" action="#{changeLocale.setFrenchLocale}"/>
Ele havia sido gerado automaticamente pelo JSF. O que nos interessa aqui é que a presença desse identificador na sequência de valores enviados pelo navegador do cliente permite que o JSF saiba que o link [Français] foi clicado. Ele então utilizará o atributo action acima para decidir como processar a sequência recebida. O atributo action="#{changeLocale.setFrenchLocale}" indica ao JSF que a solicitação do cliente deve ser processada pelo método [setFrenchLocale] de um objeto chamado changeLocale. Lembramos que esse bean foi definido por meio de anotações na classe Java [ChangeLocale]:
@ManagedBean
@SessionScoped
public class ChangeLocale implements Serializable{
O nome de um bean é definido pelo atributo name da anotação @ManagedBean. Na ausência desse atributo, o nome da classe é usado como nome do bean, com o primeiro caractere em minúscula.
Voltemos à solicitação do navegador:
![]() |
e à tag <h:commandLink> que gerou o link [Français] no qual clicamos:
<h:commandLink value="#{msg['welcome.langue1']}" action="#{changeLocale.setFrenchLocale}"/>
O controlador transmitirá a solicitação do navegador ao gerenciador de eventos definido pelo atributo action da tag <h:commandLink>. O gerenciador de eventos M referenciado pelo atributo action de um comando <h:commandLink> deve ter a seguinte assinatura:
- ele não recebe nenhum parâmetro. Veremos que, mesmo assim, ele pode ter acesso à solicitação do cliente,
- e deve retornar um resultado C do tipo String. Essa sequência de caracteres C pode ser:
- ou o nome de uma página JSF do projeto;
- ou um nome definido nas regras de navegação do arquivo [faces-config.xml] e associado a uma página JSF do projeto;
- ou um ponteiro nulo, caso o navegador do cliente não deva mudar de página,
Na arquitetura JSF acima, o controlador [Faces Servlet] utilizará a string C retornada pelo gerenciador de eventos e, eventualmente, seu arquivo de configuração [faces-config.xml] para determinar qual página JSF, deve enviar em resposta ao cliente [4].
Na tag
<h:commandLink value="#{msg['welcome.langue1']}" action="#{changeLocale.setFrenchLocale}"/>
o gerenciador do evento “clique no link” [Français] é o método [changeLocale.setFrenchLocale], onde changeLocale é uma instância da classe [utils.ChangeLocale] já estudada:
package utils;
import java.io.Serializable;
import javax.enterprise.context.SessionScoped;
import javax.faces.bean.ManagedBean;
@ManagedBean
@SessionScoped
public class ChangeLocale implements Serializable{
// a configuração regional das páginas
private String locale="fr";
public ChangeLocale() {
}
public String setFrenchLocale(){
locale="fr";
return null;
}
public String setEnglishLocale(){
locale="en";
return null;
}
public String getLocale() {
return locale;
}
}
O método setFrenchLocale possui, de fato, a assinatura dos manipuladores de eventos. Lembremos que o manipulador de eventos deve processar a solicitação do cliente. Como ele não recebe parâmetros, como pode ter acesso a ela? Existem várias maneiras de fazer isso:
- O bean B, que contém o gerenciador de eventos da página JSF P, costuma ser também aquele que contém o modelo M dessa página. Isso significa que o bean B contém campos que serão inicializados pelos valores inseridos na página P. Isso será feito pelo controlador [Faces Servlet] antes que o gerenciador de eventos do bean B seja chamado. Esse gerenciador terá, portanto, acesso, por meio dos campos do bean B ao qual pertence, aos valores inseridos pelo cliente no formulário e poderá processá-los.
- O método estático [FacesContext.getCurrentInstance()], do tipo [FacesContext], fornece acesso ao contexto de execução da solicitação JSF atual, que é um objeto do tipo [FacesContext]. O contexto de execução da consulta assim obtido permite acessar os parâmetros enviados ao servidor pelo navegador do cliente por meio do seguinte método:
Se os parâmetros enviados (POST) pelo navegador do cliente forem os seguintes:
o método getRequestParameterMap() retornará o seguinte dicionário:
chave | valor |
formulário | formulário |
javax.faces.ViewState | ... |
formulário:j_id_id21 | formulário:j_id_id21 |
Na tag
<h:commandLink value="#{msg['welcome.langue1']}" action="#{changeLocale.setFrenchLocale}"/>
o que se espera do gerenciador de eventos locale.setFrenchLocale? Quer-se que ele defina o idioma utilizado pelo aplicativo. No jargão Java, isso é chamado de “localizar” o aplicativo. Essa localização é utilizada pela tag <f:view> da página JSF [index.xhtml]:
<f:view locale="#{changeLocale.locale}">
...
</f:view>
Para mudar a página para o francês, basta que o atributo locale tenha o valor fr. Para mudá-la para o inglês, é preciso atribuir-lhe o valor en. O valor do atributo **locale é obtido pela expressão *[ChangeLocale].getLocale()*. Essa expressão retorna o valor do campo locale da classe [ChangeLocale]. A partir disso, deduz-se o código do método [ChangeLocale].setFrenchLocale(), que deve exibir as páginas em francês:
public String setFrenchLocale(){
locale="fr";
return null;
}
Já explicamos que um gerenciador de eventos deve retornar uma sequência de caracteres C que será utilizada pelo [Faces Servlet] para localizar a página JSF a ser enviada como resposta ao navegador do cliente. Se a página a ser retornada for a mesma que está sendo processada, o gerenciador de eventos pode simplesmente retornar o valor null. É isso que é feito aqui na linha 3: queremos retornar a mesma página [index.xhtml], mas em um idioma diferente.
Voltemos à arquitetura de processamento da solicitação:
![]() |
O gerenciador de eventos changeLocale.setFrenchLocale foi executado e retornou o valor null ao controlador [Faces Servlet]. Este, portanto, exibirá novamente a página [index.xhtml]. Vamos revisá-la:
<?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://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<f:view locale="#{changeLocale.locale}">
<head>
<title><h:outputText value="#{msg['welcome.titre']}" /></title>
</head>
<body>
<h:form id="formulaire">
<h:panelGrid columns="2">
<h:commandLink value="#{msg['welcome.langue1']}" action="#{changeLocale.setFrenchLocale}"/>
<h:commandLink value="#{msg['welcome.langue2']}" action="#{changeLocale.setEnglishLocale}"/>
</h:panelGrid>
<h1><h:outputText value="#{msg['welcome.titre']}" /></h1>
<h:commandLink value="#{msg['welcome.page1']}" action="page1"/>
</h:form>
</body>
</f:view>
</html>
Sempre que um valor do tipo #{msg['...']} é avaliado, um dos arquivos de mensagens [messages.properties] é utilizado. O arquivo utilizado é aquele que corresponde à “localização” da página (linha 6). Como o gerenciador de eventos changeLocale.setFrenchLocale define essa localização como fr, será utilizado o arquivo [messages_fr.properties]. Um clique no link [Anglais] (linha 14) alterará a localização para en (ver método changeLocale.setEnglishLocale). Nesse caso, será utilizado o arquivo [messages_en.properties] e a página será exibida em inglês:
![]() | ![]() |
Sempre que a página [index.xhtml] for exibida, a tag <f:view> será executada:
<f:view locale="#{changeLocale.locale}">
e, portanto, o método [ChangeLocale].getLocale() é executado novamente. Como atribuímos o escopo “Session” ao nosso bean:
@ManagedBean
@SessionScoped
public class ChangeLocale implements Serializable{
a localização definida durante uma solicitação é mantida para as solicitações seguintes.
Resta-nos analisar um último elemento da página [index.xhtml]:
<?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://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<f:view locale="#{changeLocale.locale}">
<head>
<title><h:outputText value="#{msg['welcome.titre']}" /></title>
</head>
<body>
<h:form id="formulaire">
<h:panelGrid columns="2">
<h:commandLink value="#{msg['welcome.langue1']}" action="#{changeLocale.setFrenchLocale}"/>
<h:commandLink value="#{msg['welcome.langue2']}" action="#{changeLocale.setEnglishLocale}"/>
</h:panelGrid>
<h1><h:outputText value="#{msg['welcome.titre']}" /></h1>
<h:commandLink value="#{msg['welcome.page1']}" action="page1"/>
</h:form>
</body>
</f:view>
</html>
A tag <h:commandLink> da linha 17 possui um atributo action igual a uma sequência de caracteres. Nesse caso, nenhum manipulador de eventos é chamado para processar a página. Passamos imediatamente para a página [page1.xhtml]. Vamos examinar o funcionamento do aplicativo nesse caso de uso:
![]() |
O usuário clica no link [Page 1]. O formulário é enviado ao controlador [Faces Servlet]. Este reconhece, na solicitação que recebe, que o link [Page 1] foi clicado. Ele examina a tag correspondente:
<h:commandLink value="#{msg['welcome.page1']}" action="page1"/>
Não há nenhum manipulador de eventos associado ao link. O controlador [Faces Servlet] passa imediatamente para a etapa [3] acima e exibe a página [page1.xhtml]:
![]() | ![]() |
2.4.7. A página JSF [page1.xhtml]
A página [page1.xhtml] envia o seguinte fluxo ao navegador do cliente:
![]() |
O código que gera essa página é o seguinte:
<?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://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<f:view locale="#{changeLocale.locale}">
<head>
<title><h:outputText value="#{msg['page1.titre']}"/></title>
</head>
<body>
<h1><h:outputText value="#{msg['page1.entete']}"/></h1>
<h:form>
<h:commandLink value="#{msg['page1.welcome']}" action="index"/>
</h:form>
</body>
</f:view>
</html>
Não há nada nesta página que já não tenha sido explicado. O leitor poderá estabelecer a correspondência entre o código JSF e a página enviada ao navegador do cliente. O link de retorno à página inicial:
<h:commandLink value="#{msg['page1.welcome']}" action="index"/>
exibirá a página [index.xhtml].
2.4.8. Execução do projeto
Nosso projeto agora está completo. Podemos compilá-lo (Clean and Build):
![]() |
- A compilação do projeto cria, na aba [Files], a pasta [target]. Nela, encontramos o arquivo [mv-jsf2-02-1.0-SNAPSHOT.war] do projeto. É esse arquivo que é implantado no servidor,
- em [WEB-INF / classes] e [2], encontram-se as classes compiladas da pasta [Source Packages] do projeto, bem como os arquivos que estavam no ramo [Other Sources] — neste caso, os arquivos de mensagens;
- em [WEB-INF / lib] [3], encontram-se as bibliotecas do projeto,
- na raiz de [WEB-INF] e [4], encontram-se os arquivos de configuração do projeto,
![]() |
- na raiz do arquivo [5], encontram-se as páginas JSF que estavam no ramo [Web Pages] do projeto;
- assim que o projeto for compilado, ele poderá ser executado ([6]). Ele será executado de acordo com sua configuração de execução ([7]),
- o servidor Tomcat será iniciado, caso ainda não estivesse em execução ([8]),
- o arquivo [mv-jsf2-02-1.0-SNAPSHOT.war] será carregado no servidor. Isso é chamado de implantação do projeto no servidor de aplicativos,
- em [9], é solicitado que um navegador seja iniciado durante a execução. Ele solicitará o contexto da aplicação [10], c.a.d. o URL e o [http://localhost:8080/mv-jsf2-02]. De acordo com as regras do arquivo [web.xml] (ver página 44), é o arquivo [faces/index.xhtml] que será fornecido ao navegador do cliente. Como o URL tem o formato [/faces/*], ele será processado pelo controlador [Faces Servlet] (ver [web.xml], página 44). Este processará a página e enviará o fluxo HTML a seguir:
![]() |
- O controlador [Faces Servlet] processará, então, os eventos que ocorrerão a partir dessa página.
2.4.9. O arquivo de configuração [faces-config.xml]
Utilizamos o seguinte arquivo [faces-config.xml]:
<?xml version='1.0' encoding='UTF-8'?>
<!-- =========== FULL CONFIGURATION FILE ================================== -->
<faces-config version="2.0"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd">
<application>
<resource-bundle>
<base-name>
messages
</base-name>
<var>msg</var>
</resource-bundle>
</application>
</faces-config>
Este é o arquivo mínimo para uma aplicação JSF 2 internacionalizada. Utilizamos aqui novas funcionalidades do JSF 2 em relação ao JSF 1:
- declarar beans e seu escopo com as anotações @ManagedBean, @RequestScoped, @SessionScoped, @ApplicationScoped,
- navegar entre páginas usando como chaves de navegação os nomes das páginas XHTML sem o sufixo xhtml.
Pode-se optar por não utilizar essas possibilidades e declarar esses elementos do projeto JSF em [faces-config.xml], assim como em JSF 1. Nesse caso, o arquivo [faces-config.xml] poderia ser o seguinte:
<?xml version='1.0' encoding='UTF-8'?>
<!-- =========== FULL CONFIGURATION FILE ================================== -->
<faces-config version="2.0"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd">
<!-- aplicativo -->
<application>
<resource-bundle>
<base-name>
messages
</base-name>
<var>msg</var>
</resource-bundle>
</application>
<!-- beans gerenciados -->
<managed-bean>
<managed-bean-name>changeLocale</managed-bean-name>
<managed-bean-class>utils.ChangeLocale</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
</managed-bean>
<!-- navegação -->
<navigation-rule>
<description/>
<from-view-id>/index.xhtml</from-view-id>
<navigation-case>
<from-outcome>p1</from-outcome>
<to-view-id>/page1.xhtml</to-view-id>
</navigation-case>
</navigation-rule>
<navigation-rule>
<description/>
<from-view-id>/page1.xhtml</from-view-id>
<navigation-case>
<from-outcome>welcome</from-outcome>
<to-view-id>/index.xhtml</to-view-id>
</navigation-case>
</navigation-rule>
</faces-config>
- linhas 20-24: declaração do bean changeLocale:
- linha 21: nome do bean;
- linha 22: nome completo da classe associada ao bean;
- linha 23: escopo do bean. Os valores possíveis são request, session, application,
- linhas 27-34: declaração de uma regra de navegação:
- linha 28: é possível descrever a regra. Aqui, isso não foi feito;
- linha 29: a página a partir da qual se navega (ponto de partida);
- linhas 30-33: um caso de navegação. Pode haver vários;
- linha 31: a chave de navegação;
- linha 32: a página para a qual se navega.
As regras de navegação podem ser exibidas de forma mais visual. Ao editar o arquivo [faces-config.xml], é possível usar a aba [PageFlow]:
![]() |
Suponhamos que estejamos usando o arquivo [faces-config.xml] anterior. Como nossa aplicação se comportaria?
- na classe [ChangeLocale], as anotações @ManagedBean e @SessionScoped desapareceriam, já que agora o bean está declarado em [faces-config],
- A navegação de [index.xhtml] para [page1.xhtml] por meio de um link ficaria assim:
<h:commandLink value="#{msg['welcome.page1']}" action="p1"/>
Ao atributo “action”, atribui-se a chave de navegação p1 definida em [faces-config];
- a navegação de [page1.xhtml] para [index.xhtml] por meio de um link ficaria assim:
<h:commandLink value="#{msg['page1.welcome']}" action="welcome"/>
Ao atributo “action”, atribui-se a chave de navegação “welcome” definida em [faces-config];
- os métodos setFrenchLocale e setEnglishLocale, que devem retornar uma chave de navegação, não precisam ser modificados, pois retornavam `null` para indicar que se permanecia na mesma página.
2.4.10. Conclusão
Voltemos ao projeto do NetBeans que criamos:
![]() |
Este projeto apresenta a seguinte arquitetura:
![]() |
Em cada projeto JSF, encontraremos os seguintes elementos:
- páginas JSF [A] que são enviadas [4] aos navegadores dos clientes pelo controlador [Faces Servlet] [3],
- arquivos de mensagens [C] que permitem alterar o idioma das páginas JSF,
- classes Java [B] que processam os eventos que ocorrem no navegador do cliente [2a, 2b] e/ou que servem de modelos para as páginas JSF e [3]. Na maioria das vezes, as camadas [métier] e [DAO] são desenvolvidas e testadas separadamente. A camada [web] é então testada com uma camada [métier] fictícia. Se as camadas [métier] e [DAO] estiverem disponíveis, trabalha-se, na maioria das vezes, com seus arquivos .jar.
- Arquivos de configuração [D] para vincular esses diversos elementos entre si. O arquivo [web.xml] foi descrito na página 44 e raramente será modificado. O mesmo vale para o [faces-config], onde sempre utilizaremos a versão simplificada.
2.5. Exemplo mv-jsf2-03: formulário de preenchimento — componentes JSF
A partir de agora, não mostraremos mais a construção do projeto. Apresentaremos projetos já prontos e explicaremos seu funcionamento. O leitor pode baixar todos os exemplos no site deste documento (ver parágrafo 1.2).
2.5.1. O aplicativo
A aplicação possui uma única visualização:
![]() |
O aplicativo apresenta os principais componentes JSF que podem ser utilizados em um formulário de preenchimento:
- a coluna [1] indica o nome da tag JSF / HTML utilizada,
- a coluna [2] apresenta um exemplo de entrada para cada uma das tags encontradas,
- a coluna [3] exibe os valores do bean que serve de modelo para a página,
- as entradas feitas em [2] são validadas pelo botão [4]. Essa validação serve apenas para atualizar o bean modelo da página. A mesma página é então reexibida. Assim, após a validação, a coluna [3] apresenta os novos valores do bean modelo, permitindo que o usuário verifique o impacto de suas entradas no modelo da página.
2.5.2. O projeto NetBeans
O projeto NetBeans do aplicativo é o seguinte:
![]() |
- em [1], os arquivos de configuração do projeto JSF,
- em [2], a única página do projeto: index.xhtml,
- em [3], uma folha de estilo [styles.css] para configurar a aparência da página [index.xhtml]
- em [4], as classes Java do projeto,
- em [5], o arquivo de mensagens do aplicativo em dois idiomas: francês e inglês.
2.5.3. O arquivo [pom.xml]
Apresentamos apenas as dependências:
<dependencies>
<dependency>
<groupId>com.sun.faces</groupId>
<artifactId>jsf-api</artifactId>
<version>2.1.7</version>
</dependency>
<dependency>
<groupId>com.sun.faces</groupId>
<artifactId>jsf-impl</artifactId>
<version>2.1.7</version>
</dependency>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-web-api</artifactId>
<version>6.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
Essas são as dependências necessárias para um projeto JSF. Nos exemplos a seguir, esse arquivo só será apresentado quando houver alterações.
2.5.4. O arquivo [web.xml]
O arquivo [web.xml] foi configurado para que a página [index.xhtml] seja a página inicial do projeto:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<context-param>
<param-name>javax.faces.STATE_SAVING_METHOD</param-name>
<param-value>client</param-value>
</context-param>
<context-param>
<param-name>javax.faces.PROJECT_STAGE</param-name>
<param-value>Development</param-value>
</context-param>
<context-param>
<param-name>javax.faces.FACELETS_SKIP_COMMENTS</param-name>
<param-value>true</param-value>
</context-param>
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>/faces/*</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>
30
</session-timeout>
</session-config>
<welcome-file-list>
<welcome-file>faces/index.xhtml</welcome-file>
</welcome-file-list>
</web-app>
- linha 30: a página [index.xhtml] é a página inicial,
- linhas 11-14: um parâmetro para o servlet [Faces Servlet]. Ele determina que os comentários em um facelet do tipo:
<!-- idiomas -->
sejam ignorados. Sem esse parâmetro, os comentários causam problemas difíceis de entender,
- linhas 3-6: um parâmetro para o servlet [Faces Servlet], que será explicado mais adiante.
2.5.5. O arquivo [faces-config.xml]
O arquivo [faces-config.xml] do aplicativo é o seguinte:
<?xml version='1.0' encoding='UTF-8'?>
<!-- =========== FULL CONFIGURATION FILE ================================== -->
<faces-config version="2.0"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd">
<application>
<resource-bundle>
<base-name>
messages
</base-name>
<var>msg</var>
</resource-bundle>
</application>
</faces-config>
- linhas 11-16: configuram o arquivo de mensagens do aplicativo.
2.5.6. O arquivo de mensagens [messages.properties]
Os arquivos de mensagens (ver [5] na captura de tela do projeto) são os seguintes:
[messages_fr.properties]
form.langue1=Fran\u00e7ais
form.langue2=Anglais
form.titre=Java Server Faces - les tags
form.headerCol1=Type
form.headerCol2=Champs de saisie
form.headerCol3=Valeurs du modèle de la page
form.loginPrompt=login :
form.passwdPrompt=mot de passe :
form.descPrompt=description :
form.selectOneListBox1Prompt=choix unique :
form.selectOneListBox2Prompt=choix unique :
form.selectManyListBoxPrompt=choix multiple :
form.selectOneMenuPrompt=choix unique :
form.selectManyMenuPrompt=choix multiple :
form.selectBooleanCheckboxPrompt=marié(e) :
form.selectManyCheckboxPrompt=couleurs préférées :
form.selectOneRadioPrompt=moyen de transport préféré :
form.submitText=Valider
form.buttonRazText=Raz
Essas mensagens são exibidas nos seguintes locais da página:
![]() |
A versão em inglês das mensagens é a seguinte:
[messages_en.properties]
form.langue1=French
form.langue2=English
form.titre=Java Server Faces - the tags
form.headerCol1=Input Type
form.headerCol2=Input Fields
form.headerCol3=Page Model Values
form.loginPrompt=login :
form.passwdPrompt=password :
form.descPrompt=description :
form.selectOneListBox1Prompt=unique choice :
form.selectOneListBox2Prompt=unique choice :
form.selectManyListBoxPrompt=multiple choice :
form.selectOneMenuPrompt=unique choice :
form.selectManyMenuPrompt=multiple choice :
form.selectBooleanCheckboxPrompt=married :
form.selectManyCheckboxPrompt=preferred colors :
form.selectOneRadioPrompt=preferred transport means :
form.submitText=Submit
form.buttonRazText=Reset
2.5.7. O modelo [Form.java] da página [index.xhtml]
![]() |
No projeto acima, a classe [Form.java] servirá como modelo ou backing bean para a página JSF [index.xhtml]. Vamos ilustrar esse conceito de modelo com um exemplo retirado da página [index.xhtml]:
<!-- linha 1 -->
<h:outputText value="inputText" styleClass="info"/>
<h:panelGroup>
<h:outputText value="#{msg['form.loginPrompt']}"/>
<h:inputText id="inputText" value="#{form.inputText}"/>
</h:panelGroup>
<h:outputText value="#{form.inputText}"/>
Na solicitação inicial da página [index.xhtml], o código acima gera a linha 2 da tabela de entradas:
![]() |
A linha 2 exibe o campo [1]; as linhas 3 a 6, o campo [2]; e a linha 7, o campo [3].
As linhas 5 e 7 utilizam uma expressão EL que envolve o bean de formulário definido na classe [Form.java] da seguinte maneira:
package forms;
import javax.enterprise.context.RequestScoped;
import javax.faces.bean.ManagedBean;
@ManagedBean
@RequestScoped
public class Form {
- a linha 7 define um bean sem nome. Esse nome será, portanto, o nome da classe que começa com letra minúscula: form,
- o bean tem escopo de solicitação. Isso significa que, em um ciclo de solicitação do cliente/resposta do servidor, ele é instanciado quando a solicitação precisar dele e removido quando a resposta ao cliente for enviada.
No código abaixo da página [index.xhtml]:
<!-- linha 1 -->
<h:outputText value="inputText" styleClass="info"/>
<h:panelGroup>
<h:outputText value="#{msg['form.loginPrompt']}"/>
<h:inputText id="inputText" value="#{form.inputText}"/>
</h:panelGroup>
<h:outputText value="#{form.inputText}"/>
as linhas 5 e 7 utilizam o valor inputText do bean form. Para compreender as ligações que unem uma página P ao seu modelo M, é preciso voltar ao ciclo de solicitação do cliente/resposta do servidor que caracteriza uma aplicação web:
![]() |
É preciso distinguir o caso em que a página P é enviada como resposta ao navegador (etapa 4), por exemplo, durante a solicitação inicial da página, do caso em que o usuário, ao provocar um evento na página P, este é processado pelo controlador [Faces Servlet] (etapa 1).
É possível distinguir esses dois casos observando-os do ponto de vista do navegador:
- na solicitação inicial da página, o navegador realiza uma operação GET sobre o URL da página;
- ao enviar os valores inseridos na página, o navegador realiza uma operação POST sobre o URL da página.
Em ambos os casos, é solicitado o mesmo URL. Dependendo da natureza da solicitação GET ou POST do navegador, o processamento da solicitação será diferente.
[cas 1 – demande initiale de la page P]
O navegador solicita o URL da página com um GET. O controlador [Faces Servlet] passará diretamente para a etapa [4] de renderização da resposta, e a página [index.xhtml] será enviada ao cliente. O controlador JSF solicitará que cada tag da página seja exibida. Tomemos como exemplo a linha 5 do código de [index.xhtml]:
<h:inputText id="inputText" value="#{form.inputText}"/>
A tag JSF <h:inputText value="valor"/> gera a tag HTML <input type="text" value="valor"/>. A classe responsável por processar essa tag encontra a expressão #{form.inputText}, que deve ser avaliada:
- se o bean form ainda não existir, ele é criado por instanciamento da classe forms.Form,
- a expressão #{form.inputText} é avaliada por meio da chamada ao método form.getInputText(),
- o texto <input id="formulário:inputText" type="text" name="formulaire:inputText" value="texto" /> é inserido no fluxo HTML, que será enviado ao cliente, supondo que o método form.getInputText() tenha retornado a string “texto”. Além disso, JSF atribuirá um nome (name) ao componente HTML incluído no fluxo. Esse nome é construído a partir dos identificadores id do componente JSF analisado e dos de seus componentes pais, neste caso a tag <h:form id="formulaire"/>.
É importante observar que, se em uma página P for utilizada a expressão #{M.champ}, em que M é o bean modelo da página P, este deve dispor do método público getChamp(). O tipo retornado por esse método deve poder ser convertido para o tipo String. Um modelo M possível e comum é o seguinte:
onde T é um tipo que pode ser convertido para o tipo String, eventualmente por meio do método toString.
Ainda no caso da exibição da página P, o processamento da linha:
<h:outputText value="#{form.inputText}"/>
será semelhante, e o seguinte fluxo HTML será criado:
Internamente no servidor, a página P é representada como uma árvore de componentes, espelhando a árvore de tags da página enviada ao cliente. Chamaremos essa árvore de “visualização” ou “ ” estado da página. Esse estado é armazenado. Ele pode ser armazenado de duas maneiras, dependendo da configuração definida no arquivo [web.xml] do aplicativo:
<web-app ...>
...
<context-param>
<param-name>javax.faces.STATE_SAVING_METHOD</param-name>
<param-value>client</param-value>
</context-param>
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
...
</web-app>
As linhas 7 a 11 definem o controlador [Faces Servlet]. Ele pode ser configurado por meio de diferentes tags <context-param>, incluindo as das linhas 3 a 6, que indicam que o estado de uma página deve ser salvo no cliente (o navegador). O outro valor possível, na linha 5, é “server”, que indica o salvamento no servidor. Esse é o valor padrão.
Quando o estado de uma página é salvo no cliente, o controlador JSF adiciona a cada página HTML que ele envia um campo oculto cujo valor é o estado atual da página. Esse campo oculto tem o seguinte formato:
<input type="hidden" name="javax.faces.ViewState" id="javax.faces.ViewState" value="H4sIAAAAAAAAANV...Bnoz8dqAAA=" />
Seu valor representa, de forma codificada, o estado da página enviada ao cliente. É importante entender que esse campo oculto faz parte do formulário da página e, portanto, fará parte dos valores enviados pelo navegador durante a validação do formulário. A partir desse campo oculto, o controlador JSF é capaz de restaurar a visualização exatamente como foi enviada ao cliente.
Quando o estado de uma página é salvo no servidor, o estado da página enviada ao cliente é salvo na sessão deste. Quando o navegador do cliente enviar os valores inseridos no formulário, ele também enviará seu token de sessão. A partir desse token, o controlador JSF recuperará o estado da página enviada ao cliente e a restaurará.
O estado de uma página JSF pode exigir várias centenas de bytes para ser codificado. Como esse estado é mantido para cada usuário do aplicativo, podem ocorrer problemas de memória se houver um grande número de usuários. Por esse motivo, optamos aqui por salvar o estado da página no cliente (ver [web.xml], parágrafo 2.5.4, página 66).
[cas 2 – traitement de la page P]
![]() |
Estamos na etapa [1] acima, na qual o controlador [Faces Servlet] receberá uma solicitação POST do navegador do cliente, ao qual ele havia enviado anteriormente a página [index.xhtml]. Estamos diante do processamento de um evento da página. Várias etapas ocorrerão antes mesmo que o evento possa ser processado em [2a]. O ciclo de processamento de uma solicitação POST pelo controlador JSF é o seguinte:
FEDCBA

- em [A], graças ao campo oculto javax.faces.ViewState, a visualização inicialmente enviada ao navegador do cliente é reconstituída. Aqui, os componentes da página recuperam o valor que tinham na página enviada. Nosso componente inputText recupera seu valor “texto”,
- em [B], os valores enviados pelo navegador do cliente são utilizados para atualizar os componentes da visualização. Assim, se no campo de entrada HTML, denominado inputText, o usuário digitou “jean”, o valor “jean” substitui o valor “texto”. A partir de agora, a visualização reflete a página tal como foi modificada pelo usuário e não mais como foi enviada ao navegador,
- em [C], os valores enviados são verificados. Suponhamos que o componente inputText anterior seja o campo de entrada para a idade. O valor inserido deverá ser um número inteiro. Os valores enviados pelo navegador são sempre do tipo String. Seu tipo final no modelo M associado à página P pode ser qualquer outro. Há, então, uma conversão de um tipo String para outro tipo T. Essa conversão pode falhar. Nesse caso, o ciclo de solicitação/resposta é encerrado e a página P, construída em [B], é reenviada ao navegador do cliente com mensagens de erro, caso o autor da página P as tenha previsto. Observe-se que o usuário visualiza a página exatamente como a preencheu, sem qualquer esforço por parte do desenvolvedor. Em outra tecnologia, como a JSP, o desenvolvedor precisa reconstruir ele mesmo a página P com os valores inseridos pelo usuário. O valor de um componente também pode passar por um processo de validação. Ainda usando o exemplo do componente inputText, que é o campo de preenchimento da idade, o valor inserido deverá ser não apenas um número inteiro, mas um número inteiro dentro de um intervalo [1,N]. Se o valor inserido passar pela etapa de conversão, ele pode não passar pela etapa de validação. Nesse caso, o ciclo de solicitação/resposta também é concluído e a página P, gerada em [B], é enviada de volta ao navegador do cliente,
- No [D], se todos os componentes da página P passarem pela etapa de conversão e validação, seus valores serão atribuídos ao modelo M da página P. Se o valor do campo de entrada gerado a partir da seguinte tag:
<h:inputText value="#{form.inputText}"/>
for “jean”, então esse valor será atribuído ao modelo de formulário da página por meio da execução do código form.setInputText("jean"). É importante observar que, no modelo M da página P, os campos privados de M que armazenam o valor de um campo de entrada de P devem possuir um método set,
- assim que o modelo M da página P for atualizado pelos valores enviados, o evento que provocou o POST da página P poderá ser processado. Essa é a etapa [E]. Observe-se que, se o manipulador desse evento pertencer ao bean M, ele terá acesso aos valores do formulário P que foram armazenados nos campos desse mesmo bean.
- A etapa [E] retornará ao controlador JSF uma chave de navegação. Em nossos exemplos, será sempre o nome da página XHTML a ser exibida, sem o sufixo .xhtml. Essa é a etapa [F]. Outra maneira de fazer isso é retornar uma chave de navegação que será pesquisada no arquivo [faces-config.xml]. Já descrevemos esse caso.
Concluímos do exposto que:
- uma página P exibe os campos C de seu modelo M por meio dos métodos [M].getC(),
- os campos C do modelo M de uma página P são inicializados com os valores inseridos na página P por meio dos métodos [M].setC(saisie). Nesta etapa, podem ocorrer processos de conversão e validação que podem falhar. Nesse caso, o evento que provocou o POST da página P não é processado e a página é reenviada ao cliente exatamente como ele a inseriu.
O modelo [Form.java] da página [index.xhtml] será o seguinte:
package forms;
import javax.enterprise.context.RequestScoped;
import javax.faces.bean.ManagedBean;
@ManagedBean
@RequestScoped
public class Form {
/** Cria uma nova instância do formulário */
public Form() {
}
// campos do formulário
private String inputText="texte";
private String inputSecret="secret";
private String inputTextArea="ligne1\nligne2\n";
private String selectOneListBox1="2";
private String selectOneListBox2="3";
private String[] selectManyListBox=new String[]{"1","3"};
private String selectOneMenu="1";
private String[] selectManyMenu=new String[]{"1","2"};
private String inputHidden="initial";
private boolean selectBooleanCheckbox=true;
private String[] selectManyCheckbox=new String[]{"1","3"};
private String selectOneRadio="2";
// eventos
public String submit(){
return null;
}
// getters e setters
...
}
Os campos das linhas 16 a 27 são utilizados nos seguintes locais do formulário:
![]() |
2.5.8. A página [index.xhtml]
A página [index.xhtml], que gera a visualização anterior, é a seguinte:
<?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://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<f:view locale="#{changeLocale.locale}">
<h:head>
<title>JSF</title>
<h:outputStylesheet library="css" name="styles.css"/>
</h:head>
<h:body style="background-image: url('${request.contextPath}/resources/images/standard.jpg');">
<h:form id="formulaire">
<!-- idiomas -->
<h:panelGrid columns="2">
<h:commandLink value="#{msg['form.langue1']}" action="#{changeLocale.setFrenchLocale}"/>
<h:commandLink value="#{msg['form.langue2']}" action="#{changeLocale.setEnglishLocale}"/>
</h:panelGrid>
<h1><h:outputText value="#{msg['form.titre']}"/></h1>
<h:panelGrid columnClasses="col1,col2,col3" columns="3" border="1">
<!-- cabeçalhos -->
<h:outputText value="#{msg['form.headerCol1']}" styleClass="entete"/>
<h:outputText value="#{msg['form.headerCol2']}" styleClass="entete"/>
<h:outputText value="#{msg['form.headerCol3']}" styleClass="entete"/>
<!-- linha 1 -->
...
<!-- linha 2 -->
...
<!-- linha 3 -->
...
<!-- linha 4 -->
...
<!-- linha 5 -->
...
<!-- linha 6 -->
...
<!-- linha 7 -->
...
<!-- linha 8 -->
...
<!-- linha 9 -->
...
<!-- linha 10 -->
...
<!-- linha 11 -->
...
<!-- linha 12 -->
...
</h:panelGrid>
<p>
<h:commandButton type="submit" id="submit" value="#{msg['form.submitText']}"/>
</p>
</h:form>
</h:body>
</f:view>
</html>
Analisaremos sucessivamente os principais componentes dessa página. Observe-se a estrutura geral de um formulário JSF:
<?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://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<f:view ...>
<h:head>
...
</h:head>
<h:body ...>
<h:form id="formulaire">
...
<h:commandButton type="submit" id="submit" value="#{msg['form.submitText']}"/>
...
</h:form>
</h:body>
</f:view>
</html>
Os componentes de um formulário devem estar dentro de uma tag <h:form> (linhas 12-16). A tag <f:view> (linhas 7-18) é necessária caso a aplicação seja internacionalizada. Além disso, um formulário deve dispor de um meio para ser enviado (POST), geralmente um link ou um botão, como na linha 14. Ele também pode ser enviado por diversos eventos (alteração de uma seleção em uma lista, mudança do campo ativo, digitação de um caractere em um campo de entrada, etc.).
2.5.9. O estilo do formulário
Para tornar as colunas da tabela do formulário mais legíveis, este é acompanhado por uma folha de estilo:
<f:view locale="#{changeLocale.locale}">
<h:head>
<title>JSF</title>
<h:outputStylesheet library="css" name="styles.css"/>
</h:head>
- linha 4: a folha de estilo da página é definida dentro da tag HTML <head>, por meio da tag:
<h:outputStylesheet library="css" name="styles.css"/>
A folha de estilo será procurada na pasta [resources]:
![]() |
Na tag:
<h:outputStylesheet library="css" name="styles.css"/>
- library é o nome da pasta que contém a folha de estilo,
- name é o nome da folha de estilo.
Vejamos um exemplo de uso dessa folha de estilo:
<h:panelGrid columnClasses="col1,col2,col3" columns="3" border="1">
A tag <h:panelGrid columns="3"/> define uma tabela com três colunas. O atributo columnClasses permite aplicar um estilo a essas colunas. Os valores col1, col2, col3 do atributo columnClasses designam os respectivos estilos das colunas 1, 2 e 3 da tabela. Esses estilos são buscados na folha de estilo da página:
.info{
font-family: Arial,Helvetica,sans-serif;
font-size: 14px;
font-weight: bold
}
.col1{
background-color: #ccccff
}
.col2{
background-color: #ffcccc
}
.col3{
background-color: #ffcc66
}
.entete{
font-family: 'Times New Roman',Times,serif;
font-size: 14px;
font-weight: bold
}
- linhas 7-9: o estilo denominado col1,
- linhas 11-13: o estilo denominado col2,
- linhas 15-17: o estilo denominado col3,
Esses três estilos definem a cor de fundo de cada uma das colunas.
- linhas 19-23: o estilo “entete” serve para definir o estilo dos textos da primeira linha da tabela:
<!-- cabeçalhos -->
<h:outputText value="#{msg['form.headerCol1']}" styleClass="entete"/>
<h:outputText value="#{msg['form.headerCol2']}" styleClass="entete"/>
<h:outputText value="#{msg['form.headerCol3']}" styleClass="entete"/>
- linhas 1-5: o estilo “info” serve para definir o estilo dos textos da primeira coluna da tabela:
<!-- linha 1 -->
<h:outputText value="inputText" styleClass="info"/>
Não nos deteremos muito sobre o uso de folhas de estilo, pois elas, por si só, merecem um livro inteiro e, além disso, sua elaboração costuma ser confiada a especialistas. No entanto, decidimos utilizar uma, bem simples, para lembrar que seu uso é indispensável.
Vejamos agora como foi definida a imagem de fundo da página:
<h:body style="background-image: url('${request.contextPath}/resources/images/standard.jpg');">
A imagem de fundo é definida pelo atributo style da tag <h:body>. Esse atributo permite definir elementos de estilo. A imagem de fundo está na pasta [resources/images/standard.jpg]:
![]() |
Essa imagem é obtida por meio do URL [/mv-jsf2-03/resources/images/standard.jpg]. Portanto, poderíamos escrever:
<h:body style="background-image: url('mv-jsf2-03/resources/images/standard.jpg');">
/mv-jsf2-03 é o contexto da aplicação. Esse contexto é definido pelo administrador do servidor web e, portanto, pode mudar. Esse contexto pode ser obtido pela expressão EL ${request.contextPath}. Por isso, é preferível usar o seguinte atributo style:
style="background-image: url('${request.contextPath}/resources/images/standard.jpg');"
que será válido independentemente do contexto.
2.5.10. Os dois ciclos de solicitação do cliente / resposta do servidor de um formulário
Voltemos ao que já foi explicado no parágrafo 2.5.7 em um caso geral e apliquemos isso ao formulário em análise. Este será testado no ambiente clássico JSF:
![]() |
Aqui, não haverá gerenciadores de eventos nem camada [métier]. As etapas [2x], portanto, não existirão. Distinguiremos o caso em que o formulário F é solicitado inicialmente pelo navegador do caso em que o usuário, ao provocar um evento no formulário F, faz com que este seja processado pelo controlador [Faces Servlet]. Existem dois ciclos de solicitação do cliente/resposta do servidor que são diferentes.
- o primeiro, correspondente à solicitação inicial da página, é acionado por uma operação GET do navegador sobre o URL do formulário,
- o segundo, correspondente ao envio dos valores inseridos na página, é acionado por uma operação POST sobre esse mesmo URL.
Dependendo da natureza da solicitação GET ou POST do navegador, o processamento da solicitação pelo controlador [Faces Servlet] difere.
[cas 1 – demande initiale du formulaire F]
O navegador solicita o URL da página com um GET. O controlador [Faces Servlet] passará diretamente para a etapa [4] de renderização da resposta. O formulário [index.xhtml] será inicializado por seu modelo [Form.java] e enviado ao cliente, que receberá a seguinte visualização:

As trocas de dados HTTP entre cliente e servidor são as seguintes nessa ocasião:
Solicitação HTTP do cliente:
Na linha 1, vemos o GET do navegador.
Resposta HTTP do servidor:
Embora não apareça aqui, a linha 7 é seguida por uma linha em branco e pelo código HTML do formulário. É esse código que o navegador interpreta e exibe.
[cas 2 – traitement des valeurs saisies dans le formulaire F]
O usuário preenche o formulário e o valida clicando no botão [Valider]. O navegador então solicita o URL do formulário com um POST. O controlador [Faces Servlet] processa essa solicitação, atualiza o modelo [Form.java] do formulário [index.xhtml] e reenvia o formulário [index.xhtml] atualizado por esse novo modelo. Vamos examinar esse ciclo com um exemplo:

Acima, o usuário inseriu seus dados e os validou. Ele recebe como resposta a seguinte visualização:

As trocas de dados entre cliente e servidor HTTP são as seguintes nesta ocasião:
Solicitação HTTP do cliente:
Na linha 1, o POST gerado pelo navegador. Na linha 14, os valores inseridos pelo usuário. É possível, por exemplo, identificar o texto inserido no campo de entrada:
Na linha 14, o campo oculto javax.faces.ViewState foi enviado. Esse campo representa, de forma codificada, o estado do formulário tal como foi enviado inicialmente ao navegador durante seu GET inicial.
Resposta HTTP do servidor:
Embora não seja mostrado aqui, a linha 6 é seguida por uma linha em branco e pelo código HTML do formulário atualizado por seu novo modelo, derivado do POST.
Vamos agora examinar os diferentes componentes desse formulário.
2.5.11. Tag <h:inputText>
A tag <h:inputText> gera uma tag HTML <input type="text" ...>.
Consideremos o seguinte código:
<!-- linha 1 -->
<h:outputText value="inputText" styleClass="info"/>
<h:panelGroup>
<h:outputText value="#{msg['form.loginPrompt']}"/>
<h:inputText id="inputText" value="#{form.inputText}"/>
</h:panelGroup>
<h:outputText value="#{form.inputText}"/>
e seu modelo [Form.java]:
private String inputText="texte";
public String getInputText() {
return inputText;
}
public void setInputText(String inputText) {
this.inputText = inputText;
}
Quando a página [index.html] é solicitada pela primeira vez, a página obtida é a seguinte:
- a linha 2 do código XHTML gera [1],
- a tag <h:panelGroup> (linhas 3-6) permite agrupar vários elementos em uma mesma célula da tabela gerada pela tag <h:panelGrid> da linha 20 do código completo da página (ver parágrafo 2.5.8). O texto [2] é gerado pela linha 4. O campo de entrada [3] é gerado pela linha [5]. Aqui, o método getInputText de [Form.java] (linhas 3 a 5 do código Java) foi utilizado para gerar o texto do campo de entrada,
- a linha 7 do código XHTML gera [4]. Mais uma vez, o método getInputText de [Form.java] é utilizado para gerar o texto [4].
O fluxo HTML gerado pela página XHTML é o seguinte:
<tr>
<td class="col1"><span class="info">inputText</span></td>
<td class="col2">login : <input id="formulaire:inputText" type="text" name="formulaire:inputText" value="texte" /></td>
<td class="col3">texte</td>
</tr>
As tags HTML <tr> e <td> são geradas pela tag <h:panelGrid> utilizada para gerar a tabela do formulário.
Agora, a seguir, vamos inserir um valor no campo de entrada [1] e enviar o formulário usando o botão [Valider] [2]. Recebemos como resposta a página [3, 4]:
![]() |
O valor do campo [1] é enviado da seguinte forma:
Em [2], o formulário é validado com o seguinte botão:
<h:commandButton id="submit" type="submit" value="#{msg['form.submitText']}"/>
A tag <h:commandButton> não possui o atributo action. Nesse caso, nenhum manipulador de evento é chamado nem nenhuma regra de navegação é aplicada. Após o processamento, a mesma página é retornada. Vamos revisar seu ciclo de processamento:
ABCDEF

- em [A], a página P é restaurada exatamente como havia sido enviada. Isso significa que o componente com id inputText é restaurado com seu valor inicial “texto”,
- em [B], os valores enviados pelo navegador (inseridos pelo usuário) são atribuídos aos componentes da página P. Aqui, o componente com id inputText recebe o valor “um novo texto”,
- em [C], ocorrem as conversões e validações. Aqui, não há nenhuma. No modelo M, o campo associado ao componente com id inputText é o seguinte:
private String inputText="texte";
Como os valores inseridos são do tipo String, não há conversão a ser feita. Além disso, nenhuma regra de validação foi criada. Criaremos algumas posteriormente.
- Em [D], os valores inseridos são atribuídos ao modelo. O campo inputText de [Form.java] recebe o valor “um novo texto”,
- em [E], nada é feito, pois nenhum manipulador de evento foi associado ao botão [Valider].
- em [F], a página P é enviada novamente ao cliente, pois o botão [Valider] não possui o atributo action. As linhas seguintes de [index.xhtml] são então executadas:
<!-- linha 1 -->
<h:outputText value="inputText" styleClass="info"/>
<h:panelGroup>
<h:outputText value="#{msg['form.loginPrompt']}"/>
<h:inputText id="inputText" value="#{form.inputText}"/>
</h:panelGroup>
<h:outputText value="#{form.inputText}"/>
As linhas 5 e 7 utilizam o valor do campo inputText do modelo, que agora é “um novo texto”. Daí a exibição obtida:
![]()
2.5.12. Tag <h:inputSecret>
A tag <h:inputSecret> gera uma tag HTML <input type="password" ...>. Trata-se de um campo de entrada semelhante ao da tag JSF <h:inputText>, com a diferença de que cada caractere digitado pelo usuário é substituído visualmente por um asterisco (*).
Consideremos o seguinte código:
<!-- linha 2 -->
<h:outputText value="inputSecret" styleClass="info"/>
<h:panelGroup>
<h:outputText value="#{msg['form.passwdPrompt']}"/>
<h:inputSecret id="inputSecret" value="#{form.inputSecret}"/>
</h:panelGroup>
<h:outputText value="#{form.inputSecret}"/>
e seu modelo no [Form.java]:
private String inputSecret="secret";
Quando a página [index.xhtml] é solicitada pela primeira vez, a página exibida é a seguinte:
- a linha 2 do código XHTML gera [1]
- o texto [2] é gerado pela linha 4. O campo de entrada [3] é gerado pela linha [5]. Normalmente, o método getInputSecret de [Form.java] deveria ter sido utilizado para gerar o texto do campo de entrada. Há uma exceção quando o campo é do tipo “senha”. A tag <h:inputSecret> serve apenas para ler uma entrada, não para exibi-la.
- A linha 7 do código XHTML gera [4]. Aqui, o método getInputSecret de [Form.java] foi utilizado para gerar o texto [4] (ver linha 1 do código Java).
O fluxo HTML gerado pela página XHTML é o seguinte:
<tr>
<td class="col1"><span class="info">inputSecret</span></td>
<td class="col2">mot de passe : <input id="formulaire:inputSecret" type="password" name="formulaire:inputSecret" value="" /></td>
<td class="col3">secret</td>
</tr>
- linha 3: a tag HTML <input type="password" .../> gerada pela tag JSF <h:inputSecret>
Agora, abaixo, vamos inserir um valor no campo de entrada [1] e enviar o formulário com o botão [Valider] [2]. Recebemos como resposta a página [3]:
![]() |
O valor do campo [1] é enviado da seguinte forma:
A validação do formulário por [2] provocou a atualização do modelo [Form.java] por meio da entrada [1]. O campo inputSecret de [Form.java] recebeu, então, o valor mdp. Como o formulário [index.xhtml] não definiu nenhuma regra de navegação nem nenhum gerenciador de eventos, ele é exibido novamente após a atualização de seu modelo. Voltamos, então, à exibição feita na solicitação inicial da página [index.xhtml], na qual simplesmente o campo inputSecret do modelo mudou de valor para [3].
2.5.13. Tag <h:inputTextArea>
A tag <h:inputTextArea> gera uma tag HTML <textarea ...>texto</textarea>. Trata-se de um campo de entrada semelhante ao da tag JSF <h:inputText>, com a diferença de que, neste caso, é possível digitar várias linhas de texto.
Consideremos o código a seguir:
<!-- linha 3 -->
<h:outputText value="inputTextArea" styleClass="info"/>
<h:panelGroup>
<h:outputText value="#{msg['form.descPrompt']}"/>
<h:inputTextarea id="inputTextArea" value="#{form.inputTextArea}" rows="4"/>
</h:panelGroup>
<h:outputText value="#{form.inputTextArea}"/>
e seu modelo em [Form.java]:
private String inputTextArea="ligne1\nligne2\n";
Quando a página [index.xhtml] é acessada pela primeira vez, a página exibida é a seguinte:
![]() |
- a linha 2 do código XHTML gera [1],
- o texto [2] é gerado pela linha 4. O campo de entrada [3] é gerado pela linha [5]. Seu conteúdo foi gerado pela chamada ao método getInputTextArea do modelo, que retornou o valor definido na linha 1 do código Java acima,
- a linha 7 do código XHTML gera [4]. Aqui, o método getInputTextArea de [Form.java] foi utilizado novamente. A string “linha1\nlinha2” continha quebras de linha \n. Elas ainda estão lá. Mas, inseridas em um fluxo HTML, são exibidas como espaços pelos navegadores. A tag HTML <textarea>, que exibe [3], interpreta corretamente as quebras de linha.
O fluxo HTML gerado pela página XHTML é o seguinte:
<tr>
<td class="col1"><span class="info">inputTextArea</span></td>
<td class="col2">description : <textarea id="formulaire:inputTextArea" name="formulaire:inputTextArea" rows="4">ligne1
ligne2
</textarea></td>
<td class="col3">ligne1
ligne2
</td>
</tr>
- linhas 3-5: a tag HTML <textarea>...</textarea>, gerada pela tag JSF <h:inputTextArea>
Agora, abaixo, vamos inserir um valor no campo de entrada [1] e enviar o formulário com o botão [Valider] [2]. Recebemos como resposta a página [3]:
![]() |
O valor do campo [1] enviado é o seguinte:
A validação do formulário por [2] provocou a atualização do modelo [Form.java] por meio da entrada [1]. O campo textArea de [Form.java] recebeu, então, o valor “Tutorial JSF\nparte1”. A exibição de [index.xhtml] mostra que o campo textArea do modelo foi efetivamente atualizado para [3].
2.5.14. Tag <h:selectOneListBox>
A tag <h:selectOneListBox> gera uma tag HTML <select>...</select>. Visualmente, ela gera uma lista suspensa ou uma lista com barra de rolagem.
Consideremos o seguinte código:
<!-- linha 4 -->
<h:outputText value="selectOneListBox (size=1)" styleClass="info"/>
<h:panelGroup>
<h:outputText value="#{msg['form.selectOneListBox1Prompt']}"/>
<h:selectOneListbox id="selectOneListBox1" value="#{form.selectOneListBox1}" size="1">
<f:selectItem itemValue="1" itemLabel="un"/>
<f:selectItem itemValue="2" itemLabel="deux"/>
<f:selectItem itemValue="3" itemLabel="trois"/>
</h:selectOneListbox>
</h:panelGroup>
<h:outputText value="#{form.selectOneListBox1}"/>
e seu modelo em [Form.java]:
private String selectOneListBox1="2";
Quando a página [index.xhtml] é acessada pela primeira vez, a página exibida é a seguinte:
![]() |
- a linha 2 do código XHTML gera [1]
- o texto [2] é gerado pela linha 4. A lista suspensa [3] é gerada pelas linhas [5-9]. É o valor do atributo size="1" que faz com que a lista exiba apenas um elemento. Se esse atributo estiver ausente, o valor padrão do atributo size é 1. Os elementos da lista foram gerados pelas tags <f:selectItem> das linhas 6 a 8. Essas tags têm a seguinte sintaxe:
<f:selectItem itemValue="valeur" itemLabel="texte"/>
O valor do atributo itemLabel é o que é exibido na lista. O valor do atributo itemValue é o valor do elemento. É esse valor que será enviado ao controlador [Faces Servlet] caso o elemento seja selecionado na lista suspensa.
O elemento exibido em [3] foi determinado pela chamada ao método getSelectOneListBox1() (linha 5). O resultado “2” obtido (linha 1 do código Java) fez com que o elemento da linha 7 da lista suspensa fosse exibido, pois seu atributo itemValue tem o valor “2”,
- a linha 11 do código XHTML gera [4]. Aqui, o método getSelectOneListBox1 de [Form.java] foi utilizado novamente.
O fluxo HTML gerado pela página XHTML é o seguinte:
<tr>
<td class="col1"><span class="info">selectOneListBox (size=1)</span></td>
<td class="col2">choix unique : <select id="formulaire:selectOneListBox1" name="formulaire:selectOneListBox1" size="1">
<option value="1">un</option>
<option value="2" selected="selected">deux</option>
<option value="3">trois</option>
</select></td>
<td class="col3">2</td>
</tr>
- linhas 3 e 7: a tag HTML <select ...>...</select> gerada pela tag JSF <h:selectOneListBox>,
- linhas 4 a 6: as tags HTML <option ...> ... </option> geradas pelas tags JSF <f:selectItem>,
- linha 5: o fato de o elemento com valor="2" estar selecionado na lista se traduz na presença do atributo selected="selected".
Agora, a seguir, vamos escolher um novo valor na lista e enviar o formulário com o botão. Recebemos como resposta a página:
![]() |
O valor do campo [1] enviado é o seguinte:
A validação do formulário por [2] provocou a atualização do modelo [Form.java] por meio da entrada [1]. O elemento HTML
<option value="3">trois</option>
foi selecionado. O navegador enviou a string “3” como valor do componente JSF, gerando a lista suspensa:
<h:selectOneListbox id="selectOneListBox1" value="#{form.selectOneListBox1}" size="1">
O controlador JSF utilizará o método setSelectOneListBox1("3") para atualizar o modelo da lista suspensa. Assim, após essa atualização, o campo do modelo [Form.java]
private String selectOneListBox1;
conterá agora o valor “3”.
Quando a página [index.xhtml] é exibida novamente após o processamento, esse valor faz com que seja exibido o [3,4] acima:
- ele determina o item da lista suspensa que deve ser exibido ([3]),
- o valor do campo selectOneListBox1 é exibido em [4].
Consideremos uma variante da tag <h:selectOneListBox>:
<!-- linha 5 -->
<h:outputText value="selectOneListBox (size=3)" styleClass="info"/>
<h:panelGroup>
<h:outputText value="#{msg['form.selectOneListBox2Prompt']}"/>
<h:selectOneListbox id="selectOneListBox2" value="#{form.selectOneListBox2}" size="3">
<f:selectItem itemValue="1" itemLabel="un"/>
<f:selectItem itemValue="2" itemLabel="deux"/>
<f:selectItem itemValue="3" itemLabel="trois"/>
<f:selectItem itemValue="4" itemLabel="quatre"/>
<f:selectItem itemValue="5" itemLabel="cinq"/>
</h:selectOneListbox>
</h:panelGroup>
<h:outputText value="#{form.selectOneListBox2}"/>
O modelo em [Form.java] da tag <h:selectOneListBox> da linha 5 é o seguinte:
private String selectOneListBox2="3";
Quando a página [index.xhtml] é solicitada pela primeira vez, a página exibida é a seguinte:
![]() |
- a linha 2 do código XHTML gera [1],
- o texto [2] é gerado pela linha 4. A lista com barra de rolagem [3] é gerada pelas linhas [5-11]. É o valor do atributo size="3" que faz com que tenhamos uma lista com barra de rolagem em vez de uma lista suspensa. Os elementos da lista foram gerados pelas tags <f:selectItem> das linhas 6 a 8,
O elemento selecionado em [3] foi determinado pela chamada ao método getSelectOneListBox2() (linha 5). O resultado “3” obtido (linha 1 do código Java) fez com que o elemento da linha 8 da lista fosse exibido, pois seu atributo itemValue tem o valor “3”,
- a linha 13 do código XHTML gera [4]. Aqui, o método getSelectOneListBox2 de [Form.java] foi utilizado novamente.
O fluxo HTML gerado pela página XHTML é o seguinte:
<tr>
<td class="col1"><span class="info">selectOneListBox (size=3)</span></td>
<td class="col2">choix unique : <select id="formulaire:selectOneListBox2" name="formulaire:selectOneListBox2" size="3">
<option value="1">un</option>
<option value="2">deux</option>
<option value="3" selected="selected">trois</option>
<option value="4">quatre</option>
<option value="5">cinq</option>
</select></td>
<td class="col3">3</td>
</tr>
- linha 6: o fato de o elemento com value="3" estar selecionado na lista resulta na presença do atributo selected="selected".
Agora, a seguir, vamos selecionar um novo valor na lista e enviar o formulário com o botão. Recebemos como resposta a página:
![]() |
O valor enviado para o campo [1] é o seguinte:
A validação do formulário por [2] provocou a atualização do modelo [Form.java] por meio da entrada [1]. O elemento HTML
<option value="5">cinq</option>
foi selecionado. O navegador enviou a string “5” como valor do componente JSF, o que gerou a lista suspensa:
<h:selectOneListbox id="selectOneListBox2" value="#{form.selectOneListBox2}" size="3">
O controlador JSF utilizará o método setSelectOneListBox2("5") para atualizar o modelo da lista. Assim, após essa atualização, o campo
private String selectOneListBox2;
conterá agora o valor “5”.
Quando a página [index.xhtml] é exibida novamente após o processamento, esse valor faz com que seja exibido o [3,4] acima:
- ele determina o item da lista que deve ser selecionado ([3]),
- o valor do campo selectOneListBox2 é exibido em [4].
2.5.15. Tag <h:selectManyListBox>
A tag <h:selectmanyListBox> gera uma tag <select multiple="multiple">...</select> que permite ao usuário selecionar vários elementos em uma lista.
Consideremos o seguinte código:
<!-- linha 6 -->
<h:outputText value="selectManyListBox (size=3)" styleClass="info"/>
<h:panelGroup>
<h:outputText value="#{msg['form.selectManyListBoxPrompt']}"/>
<h:selectManyListbox id="selectManyListBox" value="#{form.selectManyListBox}" size="3">
<f:selectItem itemValue="1" itemLabel="un"/>
<f:selectItem itemValue="2" itemLabel="deux"/>
<f:selectItem itemValue="3" itemLabel="trois"/>
<f:selectItem itemValue="4" itemLabel="quatre"/>
<f:selectItem itemValue="5" itemLabel="cinq"/>
</h:selectManyListbox>
<p><input type="button" value="#{msg['form.buttonRazText']}" onclick="this.form['formulaire:selectManyListBox'].selectedIndex=-1;" /></p>
</h:panelGroup>
<h:outputText value="#{form.selectManyListBoxValue}"/>
e seu modelo em [Form.java]:
private String[] selectManyListBox=new String[]{"1","3"};
Quando a página [index.xhtml] é solicitada pela primeira vez, a página exibida é a seguinte:
![]() |
- a linha 2 do código XHTML gera [1]
- o texto [2] é gerado pela linha 4. A lista [3] é gerada pelas linhas [5-11]. O atributo size="3" faz com que a lista exiba, a qualquer momento, três desses elementos. Os elementos selecionados na lista foram determinados pela chamada ao método getSelectManyListBox() (linha 5) do modelo Java. O resultado {"1","3"} obtido (linha 1 do código Java) é um array de elementos do tipo String. Cada um desses elementos serve para selecionar um dos elementos da lista. Aqui, os elementos das linhas 6 e 10 cujo atributo itemValue está na matriz {"1","3"} serão selecionados. É isso que mostra [3].
- A linha 14 do código XHTML gera [4]. Aqui, não é chamado o método getSelectManyListBox do modelo Java da lista, mas sim o seguinte método getSelectManyListBoxValue:
private String[] selectManyListBox=new String[]{"1","3"};
...
// getters e setters
public String getSelectManyListBoxValue(){
return getValue(selectManyListBox);
}
private String getValue(String[] chaines){
String value="[";
for(String chaine : chaines){
value+=" "+chaine;
}
return value+"]";
}
Se tivéssemos chamado o método getSelectManyListBox, teríamos obtido um array de String. Para incluir esse elemento no fluxo HTML, o controlador teria chamado seu método toString. No entanto, esse método, quando aplicado a um array, apenas retorna o “hashcode” do mesmo e não a lista de seus elementos, como desejamos. Por isso, utilizamos o método getSelectManyListBoxValue acima para obter uma sequência de caracteres que represente o conteúdo do array,
- a linha 12 do código XHTML gera o botão [5]. Quando esse botão é clicado, o código JavaScript do atributo onclick é executado. Ele será incorporado à página HTML, que será gerada pelo código JSF. Para compreender isso, precisamos conhecer a natureza exata dessa página.
O fluxo HTML gerado pela página XHTML é o seguinte:
<tr>
<td class="col1"><span class="info">selectManyListBox (size=3)</span></td>
<td class="col2">choix multiple : <select id="formulaire:selectManyListBox" name="formulaire:selectManyListBox" multiple="multiple" size="3">
<option value="1" selected="selected">un</option>
<option value="2">deux</option>
<option value="3" selected="selected">trois</option>
<option value="4">quatre</option>
<option value="5">cinq</option>
</select>
<p><input type="button" value="Raz" onclick="this.form['formulaire:selectManyListBox'].selectedIndex=-1;" /></p>
</td>
<td class="col3">[ 1 3]</td>
</tr>
- linhas 3 e 9: a tag HTML <select multiple="multiple"...>...</select> gerada pela tag JSF <h:selectManyListBox>. É a presença do atributo multiple que indica que se trata de uma lista de seleção múltipla,
- o fato de o modelo da lista ser a matriz de String {"1","3"} faz com que os elementos da lista nas linhas 4 (value="1") e 6 (value="3") tenham o atributo selected="selected",
- linha 10: ao clicar no botão [Raz], o código JavaScript do atributo onclick é executado. A página é representada no navegador por uma árvore de objetos frequentemente chamada de DOM (Document Object Model). Cada objeto da árvore é acessível ao código JavaScript por meio de seu atributo name. A lista na linha 3 do código HTML acima é chamada de formulário:selectManyListBox. O próprio formulário pode ser referido de várias maneiras. Aqui, ele é referido pela notação this.form, em que `this` designa o botão [Raz] e this.form o formulário no qual esse botão se encontra. A lista formulário:selectManyListBox está localizada nesse mesmo formulário. Assim, a notação this.form['formulaire:selectManyListBox'] indica a localização da lista na árvore de componentes do formulário. O objeto que representa uma lista possui um atributo selectedIndex cujo valor é o número do elemento selecionado na lista. Esse número começa em 0 para indicar o primeiro elemento da lista. O valor -1 indica que nenhum elemento está selecionado na lista. O código JavaScript que atribui o valor -1 ao atributo selectedIndex tem como efeito desmarcar todos os elementos da lista, caso haja algum.
Agora, a seguir, vamos selecionar novos valores na lista (para selecionar vários elementos na lista, mantenha a tecla Ctrl pressionada enquanto clica) e enviar o formulário com o botão [Valider] [2]. Recebemos como resposta a página [3,4]:
![]() |
O valor do campo [1] enviado é o seguinte:
A validação do formulário por [2] provocou a atualização do modelo [Form.java] por meio da entrada [1]. Os elementos HTML
<option value="3">trois</option>
<option value="4">quatre</option>
<option value="5">cinq</option>
foram selecionados. O navegador enviou as três cadeias de caracteres “3”, “4” e “5” como valores do componente JSF, que gerou a lista suspensa:
<h:selectManyListbox id="selectManyListBox" value="#{form.selectManyListBox}" size="3">
O método setSelectManyListBox do modelo será utilizado para atualizar esse modelo com os valores enviados pelo navegador:
private String[] selectManyListBox;
....
public void setSelectManyListBox(String[] selectManyListBox) {
this.selectManyListBox = selectManyListBox;
}
Na linha 3, vemos que o parâmetro do método é um array de String. Neste caso, será o array {"3","4","5"}. Após essa atualização, o campo
private String[] selectManyListBox;
agora contém a matriz {"3","4","5"}.
Quando a página [index.xhtml] é exibida novamente após o processamento, esse valor faz com que seja exibido o [3,4] acima:
- ela determina quais itens da lista devem ser selecionados ([3]),
- o valor do campo selectManyListBox é exibido em [4].
2.5.16. Tag <h:selectOneMenu>
A tag <h:selectOneMenu> é idêntica à tag <h:selectOneListBox size="1">. No exemplo, o código JSF executado é o seguinte:
<!-- linha 7 -->
<h:outputText value="selectOneMenu" styleClass="info"/>
<h:panelGroup>
<h:outputText value="#{msg['form.selectOneMenuPrompt']}"/>
<h:selectOneMenu id="selectOneMenu" value="#{form.selectOneMenu}">
<f:selectItem itemValue="1" itemLabel="un"/>
<f:selectItem itemValue="2" itemLabel="deux"/>
<f:selectItem itemValue="3" itemLabel="trois"/>
<f:selectItem itemValue="4" itemLabel="quatre"/>
<f:selectItem itemValue="5" itemLabel="cinq"/>
</h:selectOneMenu>
</h:panelGroup>
<h:outputText value="#{form.selectOneMenu}"/>
O modelo da tag <h:selectOneMenu> em [Form.java] é o seguinte:
private String selectOneMenu="1";
Na solicitação inicial da página [index.xhtml], o código anterior gera a visualização:
![]() |
Um exemplo de execução poderia ser o seguinte:
![]() |
O valor enviado para o campo [1] é o seguinte:
2.5.17. Tag <h:selectManyMenu>
A tag <h:selectManyMenu> é idêntica à tag <h:selectManyListBox size="1">. O código JSF executado no exemplo é o seguinte:
<!-- linha 8 -->
<h:outputText value="selectManyMenu" styleClass="info"/>
<h:panelGroup>
<h:outputText value="#{msg['form.selectManyMenuPrompt']}" styleClass="prompt" />
<h:selectManyMenu id="selectManyMenu" value="#{form.selectManyMenu}" >
<f:selectItem itemValue="1" itemLabel="un"/>
<f:selectItem itemValue="2" itemLabel="deux"/>
<f:selectItem itemValue="3" itemLabel="trois"/>
<f:selectItem itemValue="4" itemLabel="quatre"/>
<f:selectItem itemValue="5" itemLabel="cinq"/>
</h:selectManyMenu>
<p><input type="button" value="#{msg['form.buttonRazText']}" onclick="this.form['formulaire:selectManyMenu'].selectedIndex=-1;" /></p>
</h:panelGroup>
<h:outputText value="#{form.selectManyMenuValue}" styleClass="prompt"/>
O modelo da tag <h:selectManyMenu> em [Form.java] é o seguinte:
private String[] selectManyMenu=new String[]{"1","2"};
Ao acessar a página [index.xhtml] pela primeira vez, o código acima gera a página:
![]() |
A lista [1] contém os textos “um”, ..., “cinco”, com os elementos “um” e “dois” selecionados. O código HTML gerado é o seguinte:
<tr>
<td class="col1"><span class="info">selectManyMenu</span></td>
<td class="col2"><span class="prompt">choix multiple : </span><select id="formulaire:selectManyMenu" name="formulaire:selectManyMenu" multiple="multiple" size="1">
<option value="1" selected="selected">un</option>
<option value="2" selected="selected">deux</option>
<option value="3">trois</option>
<option value="4">quatre</option>
<option value="5">cinq</option>
</select>
<p><input type="button" value="Raz" onclick="this.form['formulaire:selectManyMenu'].selectedIndex=-1;" /></p>
</td>
<td class="col3"><span class="prompt">[ 1 2]</span></td>
</tr>
Vê-se acima, nas linhas 4 e 5, que os elementos “um” e “dois” estão selecionados (presença do atributo selected).
É difícil apresentar uma captura de tela de um exemplo de execução, pois não é possível mostrar os elementos selecionados no menu. O leitor é convidado a fazer o teste por conta própria (para selecionar vários elementos na lista, mantenha a tecla Ctrl pressionada enquanto clica).
2.5.18. Tag <h:inputHidden>
A tag <h:inputHidden> não possui representação visual. Ela serve apenas para inserir uma tag <input type="hidden" value="..."/> no fluxo da página. Incluídos dentro de uma tag <h:form>, seus valores fazem parte dos dados enviados ao servidor quando o formulário é enviado. Como se trata de campos de formulário que o usuário não vê, eles são chamados de campos ocultos. A finalidade desses campos é preservar a memória entre os diferentes ciclos de solicitação/resposta de um mesmo cliente:
- o cliente solicita um formulário F. O servidor o envia e insere uma informação I em um campo oculto C, na forma <h:inputHidden id="C" value="I"/>,
- quando o cliente preenche o formulário F e o envia ao servidor, o valor I do campo C é devolvido ao servidor. Este pode então recuperar a informação I que havia armazenado na página. Assim, criou-se uma memória entre os dois ciclos de solicitação/resposta,
- o próprio JSF utiliza essa técnica. A informação I que ele armazena no formulário F é o valor de todos os componentes deste. Para isso, ele utiliza o seguinte campo oculto:
<input type="hidden" name="javax.faces.ViewState" id="javax.faces.ViewState" value="H4sIAAAAAAAAANV...8PswawAA" />
O campo oculto se chama javax.faces.ViewState e seu valor é uma sequência de caracteres que representa, de forma codificada, o valor de todos os componentes da página enviada ao cliente. Quando o cliente reenvia a página após preencher o formulário, o campo oculto javax.faces.ViewState é reenviado com os valores inseridos. É isso que permite que o controlador JSF recrie a página exatamente como ela havia sido enviada inicialmente. Esse mecanismo foi explicado na página 72.
O código JSF do exemplo é o seguinte:
<!-- linha 9 -->
<h:outputText value="inputHidden" styleClass="info"/>
<h:inputHidden id="inputHidden" value="#{form.inputHidden}"/>
<h:outputText value="#{form.inputHidden}"/>
O modelo da tag <h:inputHidden> em [Form.java] é o seguinte:
private String inputHidden="initial";
O que resulta na seguinte exibição durante a solicitação inicial da página [index.xhtml]:
- a linha 2 gera [1], a linha 4 gera [2]. A linha 3 não gera nenhum elemento visual.
O código HTML gerado é o seguinte:
<tr>
<td class="col1"><span class="info">inputHidden</span></td>
<td class="col2"><input id="formulaire:inputHidden" type="hidden" name="formulaire:inputHidden" value="initial" /></td>
<td class="col3">initial</td>
</tr>
No momento do envio do formulário com o código POST, o valor “inicial” do campo denominado formulário:inputHidden da linha 3 será enviado junto com os demais valores do formulário. O campo
private String inputHidden;
será atualizado com esse valor, que é o mesmo que ele já possuía inicialmente. Esse valor será incluído na nova página enviada ao cliente. Assim, obtém-se sempre a captura de tela acima.
O valor enviado para o campo oculto é o seguinte:
2.5.19. Tag <h:selectBooleanCheckBox>
A tag <h:selectBooleanCheckBox> gera uma tag HTML <input type="checkbox" ...>.
Consideremos o seguinte código JSF:
<!-- linha 10 -->
<h:outputText value="selectBooleanCheckbox" styleClass="info"/>
<h:panelGroup>
<h:outputText value="#{msg['form.selectBooleanCheckboxPrompt']}" styleClass="prompt" />
<h:selectBooleanCheckbox id="selectBooleanCheckbox" value="#{form.selectBooleanCheckbox}"/>
</h:panelGroup>
<h:outputText value="#{form.selectBooleanCheckbox}"/>
O modelo da tag <h:selectBooleanCheckbox> da linha 5 acima, em [Form.java], é o seguinte:
private boolean selectBooleanCheckbox=true;
Quando a página [index.xhtml] é solicitada pela primeira vez, a página obtida é a seguinte:
![]() |
- a linha 2 do código XHTML gera [1],
- o texto [2] é gerado pela linha 4. A caixa de seleção [3] é gerada pela linha [5]. Aqui, o método getSelectBooleanCheckbox de [Form.java] foi utilizado para marcar ou não a caixa de seleção. Como o método retornou o valor booleano true (ver código Java), a caixa de seleção foi marcada,
- a linha 7 do código XHTML gera [4]. Mais uma vez, é o método getSelectBooleanCheckbox de [Form.java] que é utilizado para gerar o texto [4].
O fluxo HTML gerado pelo código JSF anterior é o seguinte:
<tr>
<td class="col1"><span class="info">selectBooleanCheckbox</span></td>
<td class="col2"><span class="prompt">marié(e) : </span>
<input id="formulaire:selectBooleanCheckbox" type="checkbox" name="formulaire:selectBooleanCheckbox" checked="checked" /></td>
<td class="col3">true</td>
</tr>
No [4], vemos a tag HTML <input type="checkbox"> que foi gerada. O valor true do modelo associado fez com que o atributo checked="checked" fosse adicionado à tag. Isso faz com que a caixa de seleção esteja marcada.
Agora, abaixo, vamos desmarcar a caixa de seleção [1], enviar o formulário [2] e observar o resultado obtido [3, 4]:
![]() |
Como a caixa de seleção está desmarcada, não há nenhum valor registrado para o campo [1].
A validação do formulário por [2] provocou a atualização do modelo [Form.java] por meio da entrada [1]. O campo selectBooleanCheckbox de [Form.java] recebeu, então, o valor false. A exibição de [index.xhtml] mostra que o campo selectBooleanCheckbox do modelo foi efetivamente atualizado para [3] e [4]. É interessante observar aqui que foi graças ao campo oculto javax.faces.ViewState que JSF conseguiu determinar que a caixa de seleção inicialmente marcada havia sido desmarcada pelo usuário. De fato, o valor de uma caixa desmarcada não faz parte dos valores enviados pelo navegador. Graças à árvore de componentes armazenada no campo oculto javax.faces.ViewState, o JSF constata que havia uma caixa de seleção chamada “selectBooleanCheckbox” no formulário e que seu valor não faz parte dos valores enviados pelo navegador do cliente. A partir disso, ele pode concluir que ela estava desmarcada no formulário enviado, o que lhe permite atribuir o valor booleano false ao modelo Java associado:
private boolean selectBooleanCheckbox;
2.5.20. Tag <h:selectManyCheckBox>
A tag <h:selectManyCheckBox> gera um grupo de caixas de seleção e, portanto, várias tags HTML <input type="checkbox" ...>. Essa tag é o equivalente à tag <h:selectManyListBox>, com a diferença de que os elementos a serem selecionados são apresentados na forma de caixas de seleção contíguas, em vez de uma lista. O que foi dito sobre a tag <h:selectManyListBox> continua válido aqui.
Consideremos o seguinte código JSF:
<!-- linha 11 -->
<h:outputText value="selectManyCheckbox" styleClass="info"/>
<h:panelGroup>
<h:outputText value="#{msg['form.selectManyCheckboxPrompt']}" styleClass="prompt" />
<h:selectManyCheckbox id="selectManyCheckbox" value="#{form.selectManyCheckbox}">
<f:selectItem itemValue="1" itemLabel="rouge"/>
<f:selectItem itemValue="2" itemLabel="bleu"/>
<f:selectItem itemValue="3" itemLabel="blanc"/>
<f:selectItem itemValue="4" itemLabel="noir"/>
</h:selectManyCheckbox>
</h:panelGroup>
<h:outputText value="#{form.selectManyCheckboxValue}"/>
O modelo da tag <h:selectManyCheckbox> da linha 5 acima, em [Form.java], é o seguinte:
private String[] selectManyCheckbox=new String[]{"1","3"};
Quando a página [index.xhtml] é solicitada pela primeira vez, a página obtida é a seguinte:
![]() |
- a linha 2 do código XHTML gera [1],
- o texto [2] é gerado pela linha 4. As caixas de seleção [3] são geradas pelas linhas 5 a 10. Para cada uma delas:
- o atributo itemLabel define o texto exibido ao lado da caixa de seleção;
- o atributo itemvalue define o valor que será enviado ao servidor se a caixa estiver marcada;
O modelo das quatro caixas de seleção é o seguinte campo Java:
private String[] selectManyCheckbox=new String[]{"1","3"};
Essa tabela define:
- quando a página é exibida, quais caixas de seleção devem estar marcadas. Isso é feito por meio de seus valores, c.a.d, e de seus campos, itemValue. No exemplo acima, as caixas de seleção cujos valores constam na tabela {"1","3"} serão marcadas. É isso que se vê na captura de tela acima;
- quando a página é enviada, o modelo selectManyCheckbox recebe a matriz com os valores das caixas de seleção que o usuário marcou. É isso que veremos a seguir,
- a linha 12 do código XHTML gera [4]. Foi o método getSelectManyCheckboxValue a seguir que gerou [4]:
public String getSelectManyCheckboxValue(){
return getValue(getSelectManyCheckbox());
}
private String getValue(String[] chaines){
String value="[";
for(String chaine : chaines){
value+=" "+chaine;
}
return value+"]";
}
O fluxo HTML gerado pelo código JSF anterior é o seguinte:
<tr>
<td>
<input name="formulaire:selectManyCheckbox" id="formulaire:selectManyCheckbox:0" value="1" type="checkbox" checked="checked" /><label for="formulaire:selectManyCheckbox:0"> rouge</label></td>
<td>
<input name="formulaire:selectManyCheckbox" id="formulaire:selectManyCheckbox:1" value="2" type="checkbox" /><label for="formulaire:selectManyCheckbox:1"> bleu</label></td>
<td>
<input name="formulaire:selectManyCheckbox" id="formulaire:selectManyCheckbox:2" value="3" type="checkbox" checked="checked" /><label for="formulaire:selectManyCheckbox:2"> blanc</label></td>
<td>
<input name="formulaire:selectManyCheckbox" id="formulaire:selectManyCheckbox:3" value="4" type="checkbox" /><label for="formulaire:selectManyCheckbox:3"> noir</label></td>
</tr>
</table></td>
<td class="col3">[ 1 3]</td>
</tr>
Quatro tags HTML <input type="checkbox" ...> foram geradas. As tags das linhas 3 e 7 possuem o atributo checked="checked", o que faz com que apareçam marcadas. Observe-se que todas elas possuem o mesmo atributo name="formulário:selectManyCheckbox"; em outras palavras, os quatro campos HTML têm o mesmo nome. Se as caixas de seleção das linhas 5 e 9 forem marcadas pelo usuário, o navegador enviará os valores das quatro caixas de seleção da seguinte forma:
e o modelo das quatro caixas de seleção
private String[] selectManyCheckbox=new String[]{"1","3"};
receberá a tabela {"2","4"}.
Vamos verificar isso a seguir. No [1], fazemos a alteração; no [2], validamos o formulário. No [3], o resultado obtido é:
![]() |
Os valores enviados para os campos [1] são os seguintes:
2.5.21. Tag <h:selectOneRadio>
A tag <h:selectOneRadio> gera um grupo de botões de opção mutuamente exclusivos.
Consideremos o seguinte código JSF:
<!-- linha 12 -->
<h:outputText value="selectOneRadio" styleClass="info"/>
<h:panelGroup>
<h:outputText value="#{msg['form.selectOneRadioPrompt']}" />
<h:selectOneRadio id="selectOneRadio" value="#{form.selectOneRadio}">
<f:selectItem itemValue="1" itemLabel="voiture"/>
<f:selectItem itemValue="2" itemLabel="vélo"/>
<f:selectItem itemValue="3" itemLabel="scooter"/>
<f:selectItem itemValue="4" itemLabel="marche"/>
</h:selectOneRadio>
</h:panelGroup>
<h:outputText value="#{form.selectOneRadio}"/>
O modelo da tag <h:selectOneRadio> da linha 5 acima é o seguinte em [Form.java]:
private String selectOneRadio="2";
Quando a página [index.xhtml] é solicitada pela primeira vez, a visualização obtida é a seguinte:
![]() |
- a linha 2 do código XHTML gera [1],
- O texto [2] é gerado pela linha 4. Os botões de opção [3] são gerados pelas linhas 5 a 10. Para cada um deles:
- o atributo itemLabel define o texto exibido ao lado do botão de opção;
- o atributo itemvalue define o valor que será enviado ao servidor se o botão estiver marcado;
O modelo dos quatro botões de opção é o seguinte campo Java:
private String selectOneRadio="2";
Esse modelo define:
- quando a página é exibida, o único botão de opção que deve estar marcado. Isso é feito por meio de seu valor, c.a.d, e de seu campo itemValue. No exemplo acima, o botão de opção com o valor “2” estará marcado. É isso que se vê na captura de tela acima;
- quando a página é enviada, o modelo selectOneRadio recebe o valor do botão de opção que foi marcado. É isso que veremos a seguir,
- a linha 12 do código XHTML gera o [4].
O fluxo HTML gerado pelo código JSF anterior é o seguinte:
<tr>
<td class="col1"><span class="info">selectOneRadio</span></td>
<td class="col2">moyen de transport préféré : <table id="formulaire:selectOneRadio">
<tr>
<td>
<input type="radio" name="formulaire:selectOneRadio" id="formulaire:selectOneRadio:0" value="1" /><label for="formulaire:selectOneRadio:0"> voiture</label></td>
<td>
<input type="radio" checked="checked" name="formulaire:selectOneRadio" id="formulaire:selectOneRadio:1" value="2" /><label for="formulaire:selectOneRadio:1"> vélo</label></td>
<td>
<input type="radio" name="formulaire:selectOneRadio" id="formulaire:selectOneRadio:2" value="3" /><label for="formulaire:selectOneRadio:2"> scooter</label></td>
<td>
<input type="radio" name="formulaire:selectOneRadio" id="formulaire:selectOneRadio:3" value="4" /><label for="formulaire:selectOneRadio:3"> marche</label></td>
</tr>
Foram geradas quatro tags HTML <input type="radio" ...>. A tag da linha 8 possui o atributo checked="checked", o que faz com que o botão de opção correspondente apareça marcado. Observe-se que todas as tags possuem o mesmo atributo name="formulário:selectOneRadio"; em outras palavras, os quatro campos HTML têm o mesmo nome. Essa é a condição para se ter um grupo de botões de opção exclusivos: quando um está marcado, os outros não estão.
Abaixo, em [1], marcamos um dos botões de opção; em [2], enviamos o formulário; em [3], o resultado obtido:
![]() |
O valor enviado para o campo [1] é o seguinte:
2.6. Exemplo mv-jsf2-04: listas dinâmicas
2.6.1. A aplicação
A aplicação é a mesma da anterior:
![]() |
As únicas alterações dizem respeito à forma como os elementos das listas das áreas [1] e [2] são gerados. Aqui, eles são gerados dinamicamente por código Java, enquanto na versão anterior estavam “fixos” no código da página JSF.
2.6.2. O projeto NetBeans
O projeto NetBeans do aplicativo é o seguinte:
![]() |
O projeto [mv-jsf2-04] é idêntico ao projeto [mv-jsf2-03], com as seguintes diferenças:
- no [1], na página JSF, os elementos das listas não serão mais definidos “fixamente” no código,
- no [2], o modelo da página JSF [1] será modificado,
- na [3], uma das mensagens será alterada.
2.6.3. A página [index.xhtml] e seu modelo [Form.java]
A página JSF [index.xhtml] passa a ser a seguinte:
<?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://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<f:view locale="#{changeLocale.locale}">
<h:head>
<title>JSF</title>
<h:outputStylesheet library="css" name="styles.css"/>
</h:head>
<h:body style="background-image: url('${request.contextPath}/resources/images/standard.jpg');">
<h:form id="formulaire">
<!-- idiomas -->
<h:panelGrid columns="2">
<h:commandLink value="#{msg['form.langue1']}" action="#{changeLocale.setFrenchLocale}"/>
<h:commandLink value="#{msg['form.langue2']}" action="#{changeLocale.setEnglishLocale}"/>
</h:panelGrid>
<h1><h:outputText value="#{msg['form.titre']}"/></h1>
<h:panelGrid columnClasses="col1,col2,col3" columns="3" border="1">
...
<!-- linha 4 -->
<h:outputText value="selectOneListBox (size=1)" styleClass="info"/>
<h:panelGroup>
<h:outputText value="#{msg['form.selectOneListBox1Prompt']}"/>
<h:selectOneListbox id="selectOneListBox1" value="#{form.selectOneListBox1}" size="1">
<f:selectItems value="#{form.selectOneListbox1Items}"/>
</h:selectOneListbox>
</h:panelGroup>
<h:outputText value="#{form.selectOneListBox1}"/>
<!-- linha 5 -->
<h:outputText value="selectOneListBox (size=3)" styleClass="info"/>
<h:panelGroup>
<h:outputText value="#{msg['form.selectOneListBox2Prompt']}"/>
<h:selectOneListbox id="selectOneListBox2" value="#{form.selectOneListBox2}" size="3">
<f:selectItems value="#{form.selectOneListbox2Items}"/>
</h:selectOneListbox>
</h:panelGroup>
<h:outputText value="#{form.selectOneListBox2}"/>
<!-- linha 6 -->
<h:outputText value="selectManyListBox (size=3)" styleClass="info"/>
<h:panelGroup>
<h:outputText value="#{msg['form.selectManyListBoxPrompt']}"/>
<h:selectManyListbox id="selectManyListBox" value="#{form.selectManyListBox}" size="3">
<f:selectItems value="#{form.selectManyListBoxItems}"/>
</h:selectManyListbox>
<p><input type="button" value="#{msg['form.buttonRazText']}" onclick="this.form['formulaire:selectManyListBox'].selectedIndex=-1;" /></p>
</h:panelGroup>
<h:outputText value="#{form.selectManyListBoxValue}"/>
<!-- linha 7 -->
<h:outputText value="selectOneMenu" styleClass="info"/>
<h:panelGroup>
<h:outputText value="#{msg['form.selectOneMenuPrompt']}"/>
<h:selectOneMenu id="selectOneMenu" value="#{form.selectOneMenu}">
<f:selectItems value="#{form.selectOneMenuItems}"/>
</h:selectOneMenu>
</h:panelGroup>
<h:outputText value="#{form.selectOneMenu}"/>
<!-- linha 8 -->
<h:outputText value="selectManyMenu" styleClass="info"/>
<h:panelGroup>
<h:outputText value="#{msg['form.selectManyMenuPrompt']}" styleClass="prompt" />
<h:selectManyMenu id="selectManyMenu" value="#{form.selectManyMenu}" >
<f:selectItems value="#{form.selectManyMenuItems}"/>
</h:selectManyMenu>
<p><input type="button" value="#{msg['form.buttonRazText']}" onclick="this.form['formulaire:selectManyMenu'].selectedIndex=-1;" /></p>
</h:panelGroup>
<h:outputText value="#{form.selectManyMenuValue}" styleClass="prompt"/>
...
<!-- linha 11 -->
<h:outputText value="selectManyCheckbox" styleClass="info"/>
<h:panelGroup>
<h:outputText value="#{msg['form.selectManyCheckboxPrompt']}" styleClass="prompt" />
<h:selectManyCheckbox id="selectManyCheckbox" value="#{form.selectManyCheckbox}">
<f:selectItems value="#{form.selectManyCheckboxItems}"/>
</h:selectManyCheckbox>
</h:panelGroup>
<h:outputText value="#{form.selectManyCheckboxValue}"/>
<!-- linha 12 -->
<h:outputText value="selectOneRadio" styleClass="info"/>
<h:panelGroup>
<h:outputText value="#{msg['form.selectOneRadioPrompt']}" />
<h:selectOneRadio id="selectOneRadio" value="#{form.selectOneRadio}">
<f:selectItems value="#{form.selectOneRadioItems}"/>
</h:selectOneRadio>
</h:panelGroup>
<h:outputText value="#{form.selectOneRadio}"/>
</h:panelGrid>
<p>
<h:commandButton type="submit" id="submit" value="#{msg['form.submitText']}"/>
</p>
</h:form>
</h:body>
</f:view>
</html>
As alterações realizadas são ilustradas pelas linhas 26 a 28. Onde antes havia o código:
<h:selectOneListbox id="selectOneListBox1" value="#{form.selectOneListBox1}" size="1">
<f:selectItem itemValue="1" itemLabel="un"/>
<f:selectItem itemValue="2" itemLabel="deux"/>
<f:selectItem itemValue="3" itemLabel="trois"/>
</h:selectOneListbox>
agora temos este:
<h:selectOneListbox id="selectOneListBox1" value="#{form.selectOneListBox1}" size="1">
<f:selectItems value="#{form.selectOneListbox1Items}"/>
</h:selectOneListbox>
As três tags <f:selectItem> das linhas 2 a 4 foram substituídas pela única tag <f:selectItems> da linha b. Essa tag possui um atributo value, cujo valor é uma coleção de elementos do tipo javax.faces.model.SelectItem. Acima, o valor do atributo value será obtido por meio da chamada do seguinte método [form].getSelectOneListbox1Items:
public SelectItem[] getSelectOneListbox1Items() {
return getItems("A",3);
}
private SelectItem[] getItems(String label, int qte) {
SelectItem[] items=new SelectItem[qte];
for(int i=0;i<qte;i++){
items[i]=new SelectItem(i,label+i);
}
return items;
}
- na linha 1, o método getSelectOneListbox1Items retorna um array de elementos do tipo javax.faces.model.SelectItem construído pelo método privado getItems da linha 5. Observe-se que o método getSelectOneListbox1Items não é o getter de um campo privado selectOneListBox1Items,
- a classe javax.faces.model.SelectItem possui diversos construtores.

Utilizamos a linha 8 do método getItems, o construtor SelectItem(Object value, String label), que corresponde à tag JSF
<f:selectItem itemValue="value" labelValue="label"/>
- linhas 5-10: o método getItems(String label, int qte) constrói um array com qte elementos do tipo SelectItem, em que o elemento i é obtido pelo construtor SelectItem(i, label+i).
O código JSF
<h:selectOneListbox id="selectOneListBox1" value="#{form.selectOneListBox1}" size="1">
<f:selectItems value="#{form.selectOneListbox1Items}"/>
</h:selectOneListbox>
torna-se, então, funcionalmente equivalente ao seguinte código JSF:
<h:selectOneListbox id="selectOneListBox1" value="#{form.selectOneListBox1}" size="1">
<f:selectItem itemValue="0" itemLabel="A0"/>
<f:selectItem itemValue="1" itemLabel="A1"/>
<f:selectItem itemValue="2" itemLabel="A2"/>
</h:selectOneListbox>
O mesmo procedimento é seguido para todas as outras listas da página JSF. Assim, no modelo [Form.java], encontram-se os seguintes novos métodos:
public SelectItem[] getSelectOneListbox1Items() {
return getItems("A",3);
}
public SelectItem[] getSelectOneListbox2Items() {
return getItems("B",4);
}
public SelectItem[] getSelectManyListBoxItems() {
return getItems("C",5);
}
public SelectItem[] getSelectOneMenuItems() {
return getItems("D",3);
}
public SelectItem[] getSelectManyMenuItems() {
return getItems("E",4);
}
public SelectItem[] getSelectManyCheckboxItems() {
return getItems("F",3);
}
public SelectItem[] getSelectOneRadioItems() {
return getItems("G",4);
}
private SelectItem[] getItems(String label, int qte) {
SelectItem[] items=new SelectItem[qte];
for(int i=0;i<qte;i++){
items[i]=new SelectItem(i,label+i);
}
return items;
}
2.6.4. O arquivo de mensagens
Apenas uma mensagem é alterada:
[messages_fr.properties]
form.titre=Java Server Faces - remplissage dynamique des listes
[messages_en.properties]
form.titre=Java Server Faces - dynamic filling of lists of elements
2.6.5. Testes
Convidamos o leitor a testar esta nova versão.
Na maioria das vezes, os elementos dinâmicos de um formulário são o resultado de um processamento de negócios ou provêm de um banco de dados:
![]() |
Vamos analisar a solicitação inicial da página JSF [index.xhtml] por meio de um GET do navegador:
- a página JSF é solicitada por [1],
- o controlador [Faces Servlet] solicita sua exibição em [3]. O mecanismo JSF, que processa a página, utiliza o modelo [Form.java] da mesma, por exemplo, o método getSelectOneListBox1Items. Esse método poderia muito bem retornar uma tabela de elementos do tipo SelectItem, a partir de informações armazenadas em um banco de dados. Para isso, ele utilizaria a camada [métier] [2b].
2.7. Exemplo mv-jsf2-05: navegação – sessão – gerenciamento de exceções
2.7.1. A aplicação
A aplicação é a mesma da anterior, com a diferença de que o formulário agora se apresenta na forma de um assistente com várias páginas:
![]() |
- em [1], a página 1 do formulário — pode ser acessada também pelo link 1 de [2]
- em [2], um grupo de 5 links.
- em [3], a página 2 do formulário, acessada pelo link 2 de [2]
![]() |
![]() |
- em [4], a página 3 do formulário acessada pelo link 3 de [2]
- em [5], a página acessada pelo link “Lançar uma exceção” de [2]
![]() |
- em [6], a página acessada pelo link 4 de [2]. Ela recapitula as entradas feitas nas páginas 1 a 3.
2.7.2. O projeto NetBeans
O projeto NetBeans do aplicativo é o seguinte:
![]() |
O projeto [mv-jsf2-05] introduz duas novidades:
- no [1], a página JSF [index.xhtml] foi dividida em três páginas [form1.xhtml, form2.xhtml, form3.xhtml], nas quais as entradas foram distribuídas. A página [form4.xhtml] é uma cópia da página [index.xhtml] do projeto anterior. Na página [2], a classe [Form.java] permanece inalterada. Ela servirá de modelo para as quatro páginas JSF anteriores,
- em [3], é adicionada uma página [exception.xhtml]: ela será utilizada quando ocorrer uma exceção na aplicação.
2.7.3. As páginas [form.xhtml] e seu modelo [Form.java]
2.7.3.1. O código das páginas XHTML
A página JSF [form1.xhtml] é a seguinte:
<?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://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<f:view locale="#{changeLocale.locale}">
<h:head>
<title>JSF</title>
<h:outputStylesheet library="css" name="styles.css"/>
</h:head>
<h:body style="background-image: url('${request.contextPath}/resources/images/standard.jpg');">
<h:form id="formulaire">
<!-- links -->
<h:panelGrid columns="2">
<h:commandLink value="#{msg['form.langue1']}" action="#{changeLocale.setFrenchLocale}"/>
<h:commandLink value="#{msg['form.langue2']}" action="#{changeLocale.setEnglishLocale}"/>
</h:panelGrid>
<h1><h:outputText value="#{msg['form1.titre']}"/></h1>
<h:panelGrid columnClasses="col1,col2" columns="2" border="1">
<h:outputText value="#{msg['form.headerCol1']}" styleClass="entete"/>
<h:outputText value="#{msg['form.headerCol2']}" styleClass="entete"/>
<!-- linha 1 -->
<h:outputText value="inputText" styleClass="info"/>
<h:panelGroup>
<h:outputText value="#{msg['form.loginPrompt']}"/>
<h:inputText id="inputText" value="#{form.inputText}"/>
</h:panelGroup>
<!-- linha 2 -->
<h:outputText value="inputSecret" styleClass="info"/>
<h:panelGroup>
<h:outputText value="#{msg['form.passwdPrompt']}"/>
<h:inputSecret id="inputSecret" value="#{form.inputSecret}"/>
</h:panelGroup>
<!-- linha 3 -->
<h:outputText value="inputTextArea" styleClass="info"/>
<h:panelGroup>
<h:outputText value="#{msg['form.descPrompt']}"/>
<h:inputTextarea id="inputTextArea" value="#{form.inputTextArea}" rows="4"/>
</h:panelGroup>
</h:panelGrid>
<!-- links -->
<h:panelGrid columns="6">
<h:commandLink value="1" action="form1"/>
<h:commandLink value="2" action="#{form.doAction2}"/>
<h:commandLink value="3" action="form3"/>
<h:commandLink value="4" action="#{form.doAction4}"/>
<h:commandLink value="#{msg['form.pagealeatoireLink']}" action="#{form.doAlea}"/>
<h:commandLink value="#{msg['form.exceptionLink']}" action="#{form.throwException}"/>
</h:panelGrid>
</h:form>
</h:body>
</f:view>
</html>
e corresponde à exibição a seguir:
![]() |
Observe os seguintes pontos:
- na linha 16, a tabela que antes tinha três colunas agora tem apenas duas. A coluna 3, que exibia os valores do modelo, foi removida. Será a [form4.xhtml] que os exibirá,
- linhas 40-46: uma tabela com seis links. Os links das linhas 44 e 46 possuem navegação estática: seu atributo `action` está codificado de forma fixa. Os demais links possuem navegação dinâmica: seu atributo `action` aponta para um método do bean de formulário responsável por retornar a chave de navegação. Os métodos referenciados em [Form.java] são os seguintes:
// eventos
public String doAction2(){
return "form2";
}
public String doAction4(){
return "form4";
}
public String doAlea(){
// um número aleatório entre 1 e 3
int i=1+(int)(3*Math.random());
// retornamos a chave de navegação
return "form"+i;
}
public String throwException() throws java.lang.Exception{
throw new Exception("Exception test");
}
Ignoraremos, por enquanto, o método throwException da linha 17. Voltaremos a ele posteriormente. Os métodos doAction2 e doAction4 limitam-se a retornar a chave de navegação sem realizar nenhum processamento. Portanto, poderíamos muito bem ter escrito:
<h:commandLink value="1" action="form1"/>
<h:commandLink value="2" action="form2"/>
<h:commandLink value="3" action="form3"/>
<h:commandLink value="4" action="form4"/>
<h:commandLink value="#{msg['form.pagealeatoireLink']}" action="#{form.doAlea}"/>
<h:commandLink value="#{msg['form.exceptionLink']}" action="#{form.throwException}"/>
Já o método doAlea gera uma chave de navegação aleatória cujo valor é escolhido no conjunto {"form1", "form2", "form3"}.
O código das páginas [form2.xhtml, form3.xhtml, form3.xhtml] é semelhante ao da página [form1.xhtml].
2.7.3.2. Vida útil do modelo [Form.java] das páginas [form*.xhtml]
Consideremos a seguinte sequência de ações:
![]() |
- na página [1], preenche-se a página 1 e passa-se para a página 3,
- em [2], preenche-se a página 3 e retorna-se à página 1,
![]() |
- em [3], a página 1 é exibida exatamente como foi preenchida. Em seguida, retorna-se à página 3,
- em [4], a página 3 é exibida exatamente como foi preenchida.
O mecanismo do campo oculto [javax.faces.ViewState] não é suficiente para explicar esse fenômeno.
Durante a transição de [1] para [2], ocorrem várias etapas:
- o modelo [Form.java] é atualizado com o POST a partir do [form1.jsp]. Notavelmente, o campo inputText recebe o valor “outro texto”,
- a chave de navegação “form3” faz com que o [form3.xhtml] seja exibido. O ViewState incorporado no [form3.xhtml] representa o estado apenas dos componentes do [form3.xhtml], e não dos do [form1.xhtml].
Ao passar de [2] para [3]:
- o modelo [Form.java] é atualizado com o POST do [form3.xhtml]. Se o tempo de vida do modelo [Form.java] estiver esgotado, um objeto [Form.java] totalmente novo é criado antes de ser atualizado pelo POST a partir do [form3.xhtml]. Nesse caso, o campo inputText do modelo retorna ao seu valor padrão:
private String inputText="texte";
e o mantém: de fato, no POST do [form3.xhtml], nada atualiza o campo inputText, que faz parte do modelo de [form1.xhtml] e não do de [form3.xhtml],
- a chave de navegação “form1” faz com que seja exibido o [form1.xhtml]. A página exibe seu modelo. No nosso caso, o campo de entrada login vinculado ao modelo inputText exibirá texte e não o valor “outro texto” inserido em [1]. Para que o campo inputText mantenha o valor inserido em [1], a duração do modelo [Form.java] deve ser “sessão” e não “solicitação”. Nesse caso,
- ao final da execução do POST a partir do [form1.xhtml], o modelo será colocado na sessão do cliente. O campo inputText terá o valor “outro texto”,
- no momento da execução do POST a partir do [form3.xhtml], o modelo será buscado nessa sessão e atualizado pelo POST a partir do [form3.xhtml]. O campo inputText não será atualizado por este POST, mas manterá o valor “outro texto” adquirido ao final do POST a partir do [form1.xhtml] e do [1].
A declaração do bean [Form.java] é, portanto, a seguinte:
package forms;
import javax.enterprise.context.SessionScoped;
import javax.faces.bean.ManagedBean;
import javax.faces.model.SelectItem;
@ManagedBean
@SessionScoped
public class Form {
A linha 8 atribui ao bean um escopo de sessão.
2.7.4. Gerenciamento de exceções
Voltemos à arquitetura geral de uma aplicação JSF:
![]() |
O que acontece quando um manipulador de eventos ou um modelo captura uma exceção proveniente da camada de negócios, como, por exemplo, uma desconexão inesperada do banco de dados?
- Os gerenciadores de eventos [2a] podem interceptar qualquer exceção proveniente da camada [métier] e fornecer ao controlador [Faces Servlet] uma chave de navegação para uma página de erro específica para a exceção,
- Para os modelos, essa solução não é viável, pois, quando eles são acionados ([3,4]), estamos na fase de renderização de uma página específica (XHTML) e não mais na fase de seleção dessa página. Como fazer para mudar de página mesmo estando na fase de renderização de uma delas? Uma solução simples, mas que nem sempre é adequada, é não tratar a exceção, que então será propagada até o contêiner de servlets que executa a aplicação. Este pode ser configurado para exibir uma página específica quando uma exceção é propagada até o contêiner de servlets. Essa solução é sempre viável e vamos analisá-la agora.
2.7.4.1. Configuração da aplicação web para o gerenciamento de exceções
A configuração de uma aplicação web para o gerenciamento de exceções é feita no arquivo [web.xml]:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<context-param>
<param-name>javax.faces.STATE_SAVING_METHOD</param-name>
<param-value>client</param-value>
</context-param>
<context-param>
<param-name>javax.faces.PROJECT_STAGE</param-name>
<param-value>Development</param-value>
</context-param>
<context-param>
<param-name>javax.faces.FACELETS_SKIP_COMMENTS</param-name>
<param-value>true</param-value>
</context-param>
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>/faces/*</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>
30
</session-timeout>
</session-config>
<welcome-file-list>
<welcome-file>faces/form1.xhtml</welcome-file>
</welcome-file-list>
<error-page>
<error-code>500</error-code>
<location>/faces/exception.xhtml</location>
</error-page>
<error-page>
<exception-type>java.lang.Exception</exception-type>
<location>/faces/exception.xhtml</location>
</error-page>
</web-app>
Nas linhas 32 a 39, encontramos a definição de duas páginas de erro. É possível ter quantas tags <error-page> forem necessárias. A tag <location> indica a página a ser exibida em caso de erro. O tipo de erro associado à página pode ser definido de duas maneiras:
- pela tag <exception-type>, que define o tipo Java da exceção tratada. Assim, a tag <error-page> das linhas 36-39 indica que, se o contêiner de servlets detectar uma exceção do tipo [java.lang.Exception] ou derivada (linha 37) durante a execução da aplicação, ele deverá exibir a página [/faces/exception.xhtml] (linha 38). Ao utilizar aqui o tipo de exceção mais genérico, [java.lang.Exception], garantimos o tratamento de todas as exceções,
- por meio da tag <error-code> (linha 33), que define um código de erro HTTP. Por exemplo, se um navegador solicitar o URL [http://machine:port/contexte/P] e a página P não existir no contexto do aplicativo, este não intervém na resposta. É o contêiner de servlets que gera essa resposta, enviando uma página de erro padrão. A primeira linha do fluxo HTTP dessa resposta contém um código de erro 404, indicando que a página P solicitada não existe. Pode-se querer gerar uma resposta que, por exemplo, respeite a identidade visual do aplicativo ou que forneça links para resolver o problema. Nesse caso, utilizar-se-á uma tag <error-page> com uma tag <error-code>404</error-code>.
Acima, o código de erro 500 HTTP é o código retornado em caso de “falha” do aplicativo. Esse é o código que seria retornado se uma exceção fosse propagada até o contêiner de servlets. As duas tags <error-page> nas linhas 28 a 35 são, portanto, provavelmente redundantes. Colocamos ambas para ilustrar as duas maneiras de lidar com um erro.
2.7.4.2. Simulação da exceção
Uma exceção é gerada artificialmente pelo link [Lancer une exception]:
![]() |
![]() |
Um clique no link [Lancer une exception] [1] faz com que a página [2] seja exibida.
No código das páginas [formx.xhtml], o link [Lancer une exception] é gerado da seguinte forma:
<!-- links -->
<h:panelGrid columns="6">
<h:commandLink value="1" action="form1"/>
...
<h:commandLink value="#{msg['form.exceptionLink']}" action="#{form.throwException}"/>
</h:panelGrid>
Na linha 5, vemos que, ao clicar no link, o método [form].throwException será executado. Este método é o seguinte:
public String throwException() throws java.lang.Exception{
throw new Exception("Exception test");
}
Nele, é lançada uma exceção do tipo [java.lang.Exception]. Ela será propagada até o contêiner de servlets, que então exibirá a página [/faces/exception.xhtml].
2.7.4.3. Informações relacionadas a uma exceção
Quando uma exceção é propagada até o contêiner de servlets, este exibe a página de erro correspondente, transmitindo a ela informações sobre a exceção. Essas informações são inseridas como novos atributos da solicitação em processamento. A solicitação de um navegador e a resposta que ele receberá são encapsuladas em objetos Java dos tipos [HttpServletRequest request] e [HttpServletResponse response]. Esses objetos estão disponíveis em todas as etapas do processamento da solicitação do navegador.
![]() |
Ao receber a solicitação HTTP do navegador, o contêiner de servlets a encapsula no objeto Java [HttpServletRequest request] e cria o objeto [HttpServletResponse response], que permitirá gerar a resposta. Nesse objeto, encontra-se, notadamente, o canal TCP-IP a ser utilizado para o fluxo HTTP da resposta. Todas as camadas t1, t2, ..., tn que intervirão no processamento do objeto request têm acesso a esses dois objetos. Cada uma delas pode acessar os elementos da solicitação inicial request e preparar a resposta, enriquecendo o objeto response. Uma camada de localisation poderá, por exemplo, definir o localisation da resposta por meio do método response.setLocale(Locale l).
As diferentes camadas podem trocar informações por meio do objeto request. Esse objeto possui um dicionário de atributos, vazio no momento da criação, que pode ser preenchido pelas camadas de processamento sucessivas. Essas camadas podem inserir nos atributos do objeto request as informações necessárias para a camada de processamento seguinte. Existem dois métodos para gerenciar os atributos do objeto request:
- void setAttribute(String s, Object o), que permite adicionar aos atributos um objeto o identificado pela string s,
- Object getAttribute(String s), que permite obter o atributo o identificado pela string s.
Quando uma exceção é propagada até o contêiner de servlets, este insere os seguintes atributos na solicitação em processamento:
chave | valor |
o código de erro HTTP, que será enviado ao cliente | |
o tipo Java da exceção, acompanhado da mensagem de erro. | |
o URL solicitado quando a exceção ocorreu | |
o servlet que estava processando a solicitação quando a exceção ocorreu |
Usaremos esses atributos da solicitação na página [exception.xhtml] para exibi-los.
2.7.4.4. A página de erro [exception.xhtml]
Seu conteúdo é o seguinte:
<?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://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<f:view locale="#{changeLocale.locale}">
<h:head>
<title>JSF</title>
<h:outputStylesheet library="css" name="styles.css"/>
</h:head>
<h:body style="background-image: url('${request.contextPath}/resources/images/standard.jpg');">
<h:form id="formulaire">
<h3><h:outputText value="#{msg['exception.header']}"/></h3>
<h:panelGrid columnClasses="col1,col2" columns="2" border="1">
<h:outputText value="#{msg['exception.httpCode']}"/>
<h:outputText value="#{requestScope['javax.servlet.error.status_code']}"/>
<h:outputText value="#{msg['exception.message']}"/>
<h:outputText value="#{requestScope['javax.servlet.error.exception']}"/>
<h:outputText value="#{msg['exception.requestUri']}"/>
<h:outputText value="#{requestScope['javax.servlet.error.request_uri']}"/>
<h:outputText value="#{msg['exception.servletName']}"/>
<h:outputText value="#{requestScope['javax.servlet.error.servlet_name']}"/>
</h:panelGrid>
<!-- links -->
<h:panelGrid columns="6">
<h:commandLink value="1" action="form1"/>
<h:commandLink value="2" action="#{form.doAction2}"/>
<h:commandLink value="3" action="form3"/>
<h:commandLink value="4" action="#{form.doAction4}"/>
<h:commandLink value="#{msg['form.pagealeatoireLink']}" action="#{form.doAlea}"/>
</h:panelGrid>
</h:form>
</h:body>
</f:view>
</html>
2.7.4.4.1. As expressões da página de exceção
Na cadeia de processamento da solicitação do cliente, a página XHTML é normalmente o último elo da cadeia:
![]() |
Todos os elementos da cadeia são classes Java, incluindo a página XHTML. Esta, de fato, é transformada em um servlet pelo contêiner de servlets, c.a.d, em uma classe Java normal. Mais especificamente, a página XHTML é transformada em código Java que é executado dentro do seguinte método:
public void _jspService(HttpServletRequest request, HttpServletResponse response)
throws java.io.IOException, ServletException {
JspFactory _jspxFactory = null;
PageContext pageContext = null;
HTTPSession session = null;
ServletContext application = null;
ServletConfig config = null;
JspWriter out = null;
Object page = this;
JspWriter _jspx_out = null;
PageContext _jspx_page_context = null;
...
...code de la page XHTML
A partir da linha 14, encontramos o código Java correspondente à página XHTML. Esse código terá uma série de objetos inicializados pelo método _jspService, linha 1 acima:
- linha 1: HttpServletRequest request: a solicitação em processamento,
- linha 1: HttpServletResponse response: a resposta que será enviada ao cliente,
- linha 7: ServletContext application: um objeto que representa a própria aplicação web. Assim como o objeto request, o objeto application pode ter atributos. Esses atributos são compartilhados por todas as solicitações de todos os clientes. Geralmente, são atributos somente de leitura,
- linha 6: HTTPSession sessão: representa a sessão do cliente. Assim como os objetos request e application, o objeto session pode ter atributos. Esses atributos são compartilhados por todas as solicitações de um mesmo cliente,
- linha 9: JspWriter out: um fluxo de gravação para o navegador do cliente. Esse objeto é útil para a depuração de uma página XHTML. Tudo o que for gravado por meio de out.println(texto) será exibido no navegador do cliente.
Quando, na página JSF, se escreve #{expressão}, a expressão pode ser a chave de um atributo dos objetos request, session ou application mencionados acima. O atributo correspondente é procurado sucessivamente nesses três objetos. Assim, #{chave} é avaliada da seguinte maneira:
- request.getAttribute(chave)
- session.getAttribute(chave)
- application.getAttribute(chave)
Assim que for encontrado um valor diferente de null, a avaliação de #{chave} é interrompida. Pode-se querer ser mais preciso, indicando o contexto no qual o atributo deve ser procurado:
- #{requestScope['clé']} para procurar o atributo no objeto request,
- #{sessionScope['clé']} para procurar o atributo no objeto session,
- #{applicationScope['clé']} para procurar o atributo no objeto application.
Foi isso que foi feito na página [exception.xhtml], página 116. Os atributos utilizados são os seguintes:
chave | domínio | valor |
solicitação | ver parágrafo 2.7.4.3. | |
idem | idem | |
idem | idem | |
idem | idem |
As diversas mensagens necessárias para a página JSF [exception.xhtml] foram adicionadas aos arquivos de mensagens já existentes:
[messages_fr.properties]
exception.header=L'exception suivante s'est produite
exception.httpCode=Code HTTP de l'erreur
exception.message=Message de l'exception
exception.requestUri=URL demandée lors de l'erreur
exception.servletName=Nom de la servlet demandée lorsque l'erreur s'est produite
[messages_en.properties]
exception.header=The following error occurred
exception.httpCode=HTTP error code
exception.message=Exception message
exception.requestUri=URL requested when error occurred
exception.servletName=Servlet requested when error occurred
2.8. Exemplo mv-jsf2-06: validação e conversão dos dados inseridos
2.8.1. O aplicativo
A aplicação apresenta um formulário de preenchimento. Após a validação do formulário, o mesmo formulário é retornado como resposta, acompanhado de eventuais mensagens de erro caso os dados inseridos tenham sido considerados incorretos.
![]() |
![]() |
2.8.2. O projeto NetBeans
O projeto NetBeans do aplicativo é o seguinte:
![]() |
O projeto [mv-jsf2-06] baseia-se novamente em uma única página [index.html] [1] e em seu modelo [Form.java] [2]. Ele continua a utilizar mensagens extraídas de [messages.properties], mas apenas em francês ([3]). A opção de mudança de idioma não está disponível.
2.8.3. O ambiente do aplicativo
Apresentamos aqui o conteúdo dos arquivos que configuram o aplicativo, sem fornecer explicações específicas. Esses arquivos permitem compreender melhor o que se segue.
[faces-config.xml]
<?xml version='1.0' encoding='UTF-8'?>
<!-- =========== FULL CONFIGURATION FILE ================================== -->
<faces-config version="2.0"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd">
<application>
<resource-bundle>
<base-name>
messages
</base-name>
<var>msg</var>
</resource-bundle>
<message-bundle>messages</message-bundle>
</application>
</faces-config>
A linha 17 é nova. Ela será explicada posteriormente.
O arquivo de mensagens [messages_fr.properties]
form.titre=Jsf - validations et conversions
saisie1.prompt=1-Nombre entier de type int
saisie2.prompt=2-Nombre entier de type int
saisie3.prompt=3-Nombre entier de type int
data.required=Vous devez entrer une donn\u00e9e
integer.required=Vous devez entrer un nombre entier
saisie4.prompt=4-Nombre entier de type int dans l'intervalle [1,10]
saisie4.error=4-Vous devez entrer un nombre entier dans l'intervalle [1,10]
saisie5.prompt=5-Nombre r\u00e9el de type double
double.required=Vous devez entrer un nombre
saisie6.prompt=6-Nombre r\u00e9el>=0 de type double
saisie6.error=6-Vous devez entrer un nombre >=0
saisie7.prompt=7-Bool\u00e9en
saisie7.error=7-Vous devez entrer un bool\u00e9en
saisie8.prompt=8-Date au format jj/mm/aaaa
saisie8.error=8-Vous devez entrer une date valide au format jj/mm/aaaa
date.required=Vous devez entrer une date
saisie9.prompt=9-Cha\u00eene de 4 caract\u00e8res
saisie9.error=9-Vous devez entrer une cha\u00eene de 4 caract\u00e8res exactement
saisie9B.prompt=9B-Heure au format hh:mm
saisie9B.error=La cha\u00eene saisie ne respecte pas le format hh:mm
submit=Valider
cancel=Annuler
saisie.type=Type de la saisie
saisie.champ=Champ de saisie
saisie.erreur=Erreur de saisie
bean.valeur=Valeurs du mod\u00e8le du formulaire
saisie10.prompt=10-Nombre entier de type int <1 ou >7
saisie10.incorrecte=10-Saisie n\u00b0 10 incorrecte
saisie10.incorrecte_detail=10-Vous devez entrer un nombre entier <1 ou >7
saisies11et12.incorrectes=La propri\u00e9t\u00e9 saisie11+saisie12=10 n'est pas v\u00e9rifi\u00e9e
saisies11et12.incorrectes_detail=La propri\u00e9t\u00e9 saisie11+saisie12=10 n'est pas v\u00e9rifi\u00e9e
saisie11.prompt=11-Nombre entier de type int
saisie12.prompt=12-Nombre entier de type int
error.sign="!"
error.sign_detail="!"
A folha de estilo [styles.css] é a seguinte:
.info{
font-family: Arial,Helvetica,sans-serif;
font-size: 14px;
font-weight: bold
}
.col1{
background-color: #ccccff
}
.col2{
background-color: #ffcccc
}
.col3{
background-color: #ffcc66
}
.col4{
background-color: #ccffcc
}
.error{
color: #ff0000
}
.saisie{
background-color: #ffcccc;
border-color: #000000;
border-width: 5px;
color: #cc0033;
font-family: cursive;
font-size: 16px
}
.entete{
font-family: 'Times New Roman',Times,serif;
font-size: 14px;
font-weight: bold
}
2.8.4. A página [index.xhtml] e seu modelo [Form.java]
A página [index.xhtml] é a seguinte:
<?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://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<h:head>
<title>JSF</title>
<h:outputStylesheet library="css" name="styles.css"/>
</h:head>
<h:body style="background-image: url('${request.contextPath}/resources/images/standard.jpg');">
<h2><h:outputText value="#{msg['form.titre']}"/></h2>
<h:form id="formulaire">
<h:messages globalOnly="true" />
<h:panelGrid columns="4" columnClasses="col1,col2,col3,col4" border="1">
<!-- linha 1 -->
<h:outputText value="#{msg['saisie.type']}" styleClass="entete"/>
<h:outputText value="#{msg['saisie.champ']}" styleClass="entete"/>
<h:outputText value="#{msg['saisie.erreur']}" styleClass="entete"/>
<h:outputText value="#{msg['bean.valeur']}" styleClass="entete"/>
<!-- linha 2 -->
<h:outputText value="#{msg['saisie1.prompt']}"/>
<h:inputText id="saisie1" value="#{form.saisie1}" styleClass="saisie"/>
<h:message for="saisie1" styleClass="error"/>
<h:outputText value="#{form.saisie1}"/>
<!-- linha 3 -->
<h:outputText value="#{msg['saisie2.prompt']}" />
<h:inputText id="saisie2" value="#{form.saisie2}" styleClass="saisie"/>
<h:message for="saisie2" showSummary="true" showDetail="false" styleClass="error"/>
<h:outputText value="#{form.saisie2}"/>
<!-- linha 4 -->
<h:outputText value="#{msg['saisie3.prompt']}" />
<h:inputText id="saisie3" value="#{form.saisie3}" styleClass="saisie" required="true" requiredMessage="#{msg['data.required']}" converterMessage="#{msg['integer.required']}"/>
<h:message for="saisie3" styleClass="error"/>
<h:outputText value="#{form.saisie3}"/>
<!-- linha 5 -->
<h:outputText value="#{msg['saisie4.prompt']}" />
<h:inputText id="saisie4" value="#{form.saisie4}" styleClass="saisie" required="true" requiredMessage="#{msg['data.required']}" converterMessage="#{msg['integer.required']}" validatorMessage="#{msg['saisie4.error']}">
<f:validateLongRange minimum="1" maximum="10" />
</h:inputText>
<h:message for="saisie4" styleClass="error"/>
<h:outputText value="#{form.saisie4}"/>
<!-- linha 6 -->
...
<!-- linha 7 -->
...
<!-- linha 8 -->
...
<!-- linha 9 -->
...
<!-- linha 10 -->
...
<!-- linha 11 -->
...
<!-- linha 12 -->
...
<!-- linha 13 -->
...
</h:panelGrid>
<!-- botões de comando -->
<h:panelGrid columns="2">
<h:commandButton value="#{msg['submit']}" action="#{form.submit}"/>
<h:commandButton value="#{msg['cancel']}" immediate="true" action="#{form.cancel}"/>
</h:panelGrid>
</h:form>
</h:body>
</html>
A principal novidade é a presença das tags:
- para exibir mensagens de erro <h:messages> (linha 14), <h:message> (linhas 24, 29, 34),
- que impõem restrições de validade às entradas <f:validateLongRange> (linha 39), <f:validateDoubleRange>, <f:validateLength>, <f:validateRegex>,
- que definem um conversor entre a entrada e seu modelo como <f:convertDateTime>.
O modelo desta página é a seguinte classe [Form.java]:
package forms;
import com.corejsf.util.Messages;
import java.util.Date;
import javax.enterprise.context.RequestScoped;
import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.validator.ValidatorException;
@ManagedBean
@RequestScoped
public class Form {
public Form() {
}
// entradas
private Integer saisie1 = 0;
private Integer saisie2 = 0;
private Integer saisie3 = 0;
private Integer saisie4 = 0;
private Double saisie5 = 0.0;
private Double saisie6 = 0.0;
private Boolean saisie7 = true;
private Date saisie8 = new Date();
private String saisie9 = "";
private Integer saisie10 = 0;
private Integer saisie11 = 0;
private Integer saisie12 = 0;
private String errorSaisie11 = "";
private String errorSaisie12 = "";
// ações
public String submit() {
...
}
public String cancel() {
...
}
// validadores
public void validateSaisie10(FacesContext context, UIComponent component, Object value) {
...
}
// getters e setters
...
}
A novidade aqui é que os campos do modelo não são mais apenas do tipo String, mas de diversos tipos.
2.8.5. Os diferentes campos de preenchimento do formulário
Analisaremos agora, sucessivamente, os diferentes campos de preenchimento do formulário.
2.8.5.1. Campos 1 a 4: inserção de um número inteiro
A página [index.xhtml] apresenta a entrada 1 da seguinte forma:
<!-- linha 2 -->
<h:outputText value="#{msg['saisie1.prompt']}"/>
<h:inputText id="saisie1" value="#{form.saisie1}" styleClass="saisie"/>
<h:message for="saisie1" styleClass="error"/>
<h:outputText value="#{form.saisie1}"/>
O modelo form.saisie1 está definido da seguinte forma no [Form.java]:
private Integer saisie1 = 0;
Em um GET do navegador, a página [index.xhtml] associada ao seu modelo [Form.java] produz visualmente o seguinte:
- a linha 2 gera [1],
- a linha 3 gera [2],
- a linha 4 gera [3],
- a linha 5 gera [4].
Suponhamos que a seguinte entrada seja feita e, em seguida, validada:
![]() |
Obtém-se, então, o seguinte resultado no formulário retornado pelo aplicativo:
![]() |
- em [1], a entrada incorreta,
- em [2], a mensagem de erro que a sinaliza,
- em [3], percebe-se que o valor do campo Integer “saisie1” do modelo não se alterou.
Vamos explicar o que aconteceu. Para isso, voltemos ao ciclo de processamento de uma página JSF:
![]() |
Analisamos esse ciclo para o componente:
<h:inputText id="saisie1" value="#{form.saisie1}" styleClass="saisie"/>
e seu modelo:
private Integer saisie1 = 0;
- em [A], a página [index.xhtml] enviada durante o GET do navegador é restaurada. Em [A], a página está exatamente como o usuário a recebeu. O componente id="saisie1" retoma seu valor inicial “0”,
- no [B], os componentes da página recebem como valores os valores enviados pelo navegador. No [B], a página está exatamente como o usuário a preencheu e validou. O componente id="saisie1" recebe como valor o valor enviado “x”,
- em [C], se a página contiver validadores e conversores explícitos, estes serão executados. Conversores implícitos também são executados se o tipo do campo associado ao componente não for do tipo String. Esse é o caso aqui, onde o campo form.saisie1 é do tipo Integer. JSF tentará transformar o valor “x” do componente id="saisie1" em um tipo Integer. Isso provocará um erro que interromperá o ciclo de processamento [A-F]. Esse erro será associado ao componente id="saisie1". Por meio do [D2], passa-se então diretamente para a fase de renderização da resposta. A mesma página [index.xhtml] é retornada,
- a fase [D] só ocorre se todos os componentes de uma página tiverem passado pela fase de conversão/validação. É nessa fase que o valor do componente id="saisie1" será atribuído ao seu modelo form.saisie1.
Se a fase [C] falhar, a página é exibida novamente e o código a seguir é executado mais uma vez:
<!-- linha 2 -->
<h:outputText value="#{msg['saisie1.prompt']}"/>
<h:inputText id="saisie1" value="#{form.saisie1}" styleClass="saisie"/>
<h:message for="saisie1" styleClass="error"/>
<h:outputText value="#{form.saisie1}"/>
![]() |
A mensagem exibida em [2] provém da linha 4 de [index.xhtml]. A tag <h:message for="idComposant"/> exibe a mensagem de erro associada ao componente indicado pelo atributo for, caso haja um erro. A mensagem exibida em [2] é padrão e está no arquivo [javax/faces/Messages.properties] do arquivo [jsf-api.jar]:
![]() |
No [2], percebe-se que o arquivo de mensagens existe em várias variantes. Vamos examinar o conteúdo do [Messages_fr.properties]:
O arquivo contém mensagens divididas em categorias:
- erros em um componente, linha 3,
- erros de conversão entre um componente e seu modelo, linha 12
- erros de validação quando há validadores na página, linha 23.
O erro ocorrido no componente id="saisie1" é do tipo erro de conversão de um tipo String para um tipo Integer. A mensagem de erro associada é a da linha 18 do arquivo de mensagens.
javax.faces.converter.IntegerConverter.INTEGER_detail={2} : «{0}» doit être un nombre compris entre -2147483648 et 2147483647. Exemple : {1}
A mensagem de erro exibida está reproduzida abaixo:
![]() |
Observa-se que, na mensagem:
- o parâmetro {2} foi substituído pelo identificador do componente para o qual ocorreu o erro de conversão,
- o parâmetro {0} foi substituído pela entrada feita em [1] para o componente,
- o parâmetro {1} foi substituído pelo número 9346.
A maioria das mensagens relacionadas aos componentes possui duas versões: uma versão resumida (summary) e uma versão detalhada (detail). É o caso das linhas 16 a 18:
A mensagem com a chave _detail (linha 2) é a chamada mensagem detalhada. A outra é a chamada mensagem resumida. A tag <h:message> exibe, por padrão, a mensagem detalhada. Esse comportamento pode ser alterado pelos atributos showSummary e showDetail. É isso que é feito para o componente com id “saisie2”:
<!-- linha 3 -->
<h:outputText value="#{msg['saisie2.prompt']}" />
<h:inputText id="saisie2" value="#{form.saisie2}" styleClass="saisie"/>
<h:message for="saisie2" showSummary="true" showDetail="false" styleClass="error"/>
<h:outputText value="#{form.saisie2}"/>
Na linha 2, o componente saisie2 está vinculado ao campo form.saisie2 a seguir:
private Integer saisie2 = 0;
O resultado obtido é o seguinte:
![]() |
- em [1], a mensagem detalhada; em [2], a mensagem resumida.
A tag <h:messages> exibe, na forma de uma lista, todas as mensagens de erro resumidas de todos os componentes, bem como as mensagens de erro não associadas a nenhum componente. Também neste caso, alguns atributos podem alterar esse comportamento padrão:
- showDetail: true / false para solicitar ou não as mensagens detalhadas,
- showSummary: true / false para solicitar ou não as mensagens resumidas,
- globalOnly: true / false para determinar se serão exibidas apenas as mensagens de erro não associadas a componentes. Uma mensagem desse tipo poderia, por exemplo, ser criada pelo desenvolvedor.
A mensagem de erro associada a uma conversão pode ser alterada de várias maneiras. Em primeiro lugar, é possível indicar ao aplicativo para usar outro arquivo de mensagens. Essa modificação é feita em [faces-config.xml]:
<faces-config ...">
<application>
<resource-bundle>
<base-name>
messages
</base-name>
<var>msg</var>
</resource-bundle>
<message-bundle>messages</message-bundle>
</application>
...
</faces-config>
As linhas 3 a 8 definem um arquivo de mensagens, mas não é esse que é utilizado pelas tags <h:message> e <h:messages>. É necessário utilizar a tag <message-bundle> da linha 9 para defini-lo. A linha 9 indica às tags <h:message(s)> que o arquivo [messages.properties] deve ser processado antes do arquivo [javax.faces.Messages.properties]. Assim, se adicionarmos as seguintes linhas ao arquivo [messages_fr.properties]:
# conversões
javax.faces.converter.IntegerConverter.INTEGER=erreur
javax.faces.converter.IntegerConverter.INTEGER_detail=erreur d\u00e9taill\u00e9e
o erro retornado para os componentes saisie1 e saisie2 passa a ser:

Outra maneira de alterar a mensagem de erro de conversão é usar o atributo converterMessage do componente, conforme mostrado abaixo para o componente saisie3:
<!-- linha 4 -->
<h:outputText value="#{msg['saisie3.prompt']}" />
<h:inputText id="saisie3" value="#{form.saisie3}" styleClass="saisie" required="true" requiredMessage="#{msg['data.required']}" converterMessage="#{msg['integer.required']}"/>
<h:message for="saisie3" styleClass="error"/>
<h:outputText value="#{form.saisie3}"/>
O componente saisie3 está vinculado ao seguinte campo form.saisie3:
private Integer saisie3 = 0;
- na linha 3, o atributo converterMessage define explicitamente a mensagem a ser exibida em caso de erro de conversão;
- linha 3, o atributo required="true" indica que o preenchimento é obrigatório. O campo não pode ficar vazio. Um campo é considerado vazio se não contiver nenhum caractere ou se contiver uma sequência de espaços. Mais uma vez, existe em [javax.faces.Messages.properties] uma mensagem padrão:
O atributo requiredMessage permite substituir essa mensagem padrão. Se o arquivo [messages.properties] contiver as seguintes mensagens:
...
data.required=Vous devez entrer une donnée
integer.required=Vous devez entrer un nombre entier
será possível obter o seguinte resultado:
![]() |
ou ainda este:
![]() |
Verificar se uma entrada corresponde a um número inteiro nem sempre é suficiente. Às vezes, é preciso verificar se o número inserido pertence a um determinado intervalo. Nesse caso, utiliza-se um validador. A entrada nº 4 é um exemplo disso. Seu código em [index.xhtml] é o seguinte:
<!-- linha 5 -->
<h:outputText value="#{msg['saisie4.prompt']}" />
<h:inputText id="saisie4" value="#{form.saisie4}" styleClass="saisie" required="true" requiredMessage="#{msg['data.required']}" converterMessage="#{msg['integer.required']}" validatorMessage="#{msg['saisie4.error']}">
<f:validateLongRange minimum="1" maximum="10" />
</h:inputText>
<h:message for="saisie4" styleClass="error"/>
<h:outputText value="#{form.saisie4}"/>
Na linha 3, o componente saisie4 está vinculado ao seguinte modelo form.saisie4:
private Integer saisie4 = 0;
Nas linhas 3 a 5, a tag <h:inputText> possui uma tag filha <f:validateLongRange> que admite dois atributos opcionais: minimum e maximum. Essa tag, também chamada de validador, permite adicionar uma restrição ao valor inserido: ele deve ser não apenas um número inteiro, mas um número inteiro no intervalo [minimum, maximum] se os dois atributos minimum e maximum estiverem presentes, ser maior ou igual a minimum se apenas o atributo minimum estiver presente, e ser menor ou igual a maximum se apenas o atributo maximum estiver presente. O validador <f:validateLongRange> possui mensagens de erro padrão em [javax.faces.Messages.properties]:
Mais uma vez, é possível substituir essas mensagens por outras. Existe um atributo validatorMessage que permite definir uma mensagem específica para o componente. Assim, com o código JSF a seguir:
<h:inputText id="saisie4" value="#{form.saisie4}" styleClass="saisie" required="true" requiredMessage="#{msg['data.required']}" converterMessage="#{msg['integer.required']}" validatorMessage="#{msg['saisie4.error']}">
<f:validateLongRange minimum="1" maximum="10" />
</h:inputText>
e a seguinte mensagem em [messages.properties]:
saisie4.error=4-Vous devez entrer un nombre entier dans l'intervalle [1,10]
obtém-se o seguinte resultado:

2.8.5.2. Entradas 5 e 6: inserção de um número real
A entrada de números reais segue regras semelhantes às da entrada de números inteiros. O código XHTML das entradas 5 e 6 é o seguinte:
<!-- linha 6 -->
<h:outputText value="#{msg['saisie5.prompt']}" />
<h:inputText id="saisie5" value="#{form.saisie5}" styleClass="saisie" required="true" requiredMessage="#{msg['data.required']}" converterMessage="#{msg['double.required']}"/>
<h:message for="saisie5" styleClass="error"/>
<h:outputText value="#{form.saisie5}"/>
<!-- linha 7 -->
<h:outputText value="#{msg['saisie6.prompt']}"/>
<h:inputText id="saisie6" value="#{form.saisie6}" styleClass="saisie" required="true" requiredMessage="#{msg['data.required']}" converterMessage="#{msg['double.required']}" validatorMessage="#{msg['saisie6.error']}">
<f:validateDoubleRange minimum="0.0"/>
</h:inputText>
<h:message for="saisie6" styleClass="error"/>
<h:outputText value="#{form.saisie6}"/>
Os elementos do modelo [Form.java] relacionados aos componentes saisie5 e saisie6:
private Double saisie5 = 0.0;
private Double saisie6 = 0.0;
As mensagens de erro associadas aos conversores e validadores dos componentes saisie5 e saisie6, no [messages.properties]:
double.required=Vous devez entrer un nombre
saisie6.error=6-Vous devez entrer un nombre >=0
Aqui está um exemplo de execução:

2.8.5.3. Entrada 7: inserção de um valor booleano
A inserção de um valor booleano normalmente deve ser feita por meio de uma caixa de seleção. Se for feita por meio de um campo de entrada, a string “true” é convertida no valor booleano true e qualquer outra string no valor booleano false.
O código XHTML do exemplo:
<!-- linha 8 -->
<h:outputText value="#{msg['saisie7.prompt']}"/>
<h:inputText id="saisie7" value="#{form.saisie7}" styleClass="saisie" required="true" requiredMessage="#{msg['data.required']}" converterMessage="#{msg['double.required']}"/>
<h:message for="saisie7" styleClass="error"/>
<h:outputText value="#{form.saisie7}"/>
O modelo do componente saisie7:
private Boolean saisie7 = true;
Aqui está um exemplo de entrada e sua resposta:
![]() |
Em [1], o valor inserido. Por conversão, essa sequência “x” se torna o valor booleano false. É o que mostra [2]. O valor [3] do modelo não se alterou. Ele só muda quando todas as conversões e validações da página forem bem-sucedidas. Esse não foi o caso neste exemplo.
2.8.5.4. Entrada 8: inserção de uma data
A inserção de uma data é feita no exemplo com o código XHTML a seguir:
<!-- linha 9 -->
<h:outputText value="#{msg['saisie8.prompt']}"/>
<h:inputText id="saisie8" value="#{form.saisie8}" styleClass="saisie" required="true" requiredMessage="#{msg['date.required']}" converterMessage="#{msg['saisie8.error']}">
<f:convertDateTime pattern="dd/MM/yyyy"/>
</h:inputText>
<h:message for="saisie8" styleClass="error"/>
<h:outputText value="#{form.saisie8}">
<f:convertDateTime pattern="dd/MM/yyyy"/>
</h:outputText>
O componente saisie8 da linha 3 utiliza um conversor java.lang.String <--> java.util.Date. O modelo form.saisie8 associado ao componente saisie8 é o seguinte:
private Date saisie8 = new Date();
O componente definido pelas linhas 7 a 9 também utiliza um conversor, mas apenas no sentido java.util.Date --> java.lang.String.
O conversor <f:convertDateTime> aceita diversos atributos, entre os quais o atributo pattern, que define o formato da sequência de caracteres que deve ser transformada em data ou o formato no qual uma data deve ser exibida.
Na solicitação inicial da página [index.xhtml], a linha 8 acima é exibida da seguinte forma:
Os campos [1] e [2] exibem, ambos, o valor do modelo form.saisie8:
private Date saisie8 = new Date();
onde saisie8 assume como valor a data de hoje. O conversor utilizado nos dois casos para a exibição da data é o seguinte:
<f:convertDateTime pattern="dd/MM/yyyy"/>
onde dd (day) indica o número do dia, MM (Month) o número do mês e yyyy (year) o ano. Em [1], o conversor é utilizado para a conversão inversa java.lang.String --> java.util.Date. A data inserida deverá, portanto, seguir o formato “dd/MM/yyyy” para ser válida.
Existem mensagens padrão para datas inválidas no [javax.faces.Messages.properties]:
que podem ser substituídas por mensagens personalizadas. Assim, no exemplo:
<h:inputText id="saisie8" value="#{form.saisie8}" styleClass="saisie" required="true" requiredMessage="#{msg['date.required']}" converterMessage="#{msg['saisie8.error']}">
<f:convertDateTime pattern="dd/MM/yyyy"/>
</h:inputText>
a mensagem exibida em caso de erro de conversão será a mensagem com a chave saisie8.error a seguir:
saisie8.error=8-Vous devez entrer une date valide au format jj/mm/aaaa
Veja um exemplo:
![]()
2.8.5.5. Entrada 9: inserção de uma sequência de caracteres com comprimento restrito
A entrada 9 mostra como impor que uma sequência inserida tenha um número de caracteres dentro de um intervalo:
<!-- linha 10 -->
<h:outputText value="#{msg['saisie9.prompt']}"/>
<h:inputText id="saisie9" value="#{form.saisie9}" styleClass="saisie" required="true" requiredMessage="#{msg['data.required']}" validatorMessage="#{msg['saisie9.error']}">
<f:validateLength minimum="4" maximum="4"/>
</h:inputText>
<h:message for="saisie9" styleClass="error"/>
<h:outputText value="#{form.saisie9}"/>
Na linha 4, o validador <f:validateLength minimum="4" maximum="4"/> exige que a sequência digitada tenha exatamente 4 caracteres. É possível usar apenas um dos atributos: minimum para um número mínimo de caracteres e maximum para um número máximo.
O modelo form.saisie9 do componente saisie9 da linha 3 é o seguinte:
private String saisie9 = "";
Existem mensagens de erro padrão para esse tipo de validação:
que podem ser substituídas usando o atributo validatorMessage, como na linha 3 acima. A mensagem da chave saisie9.error é a seguinte:
saisie9.error=9-Vous devez entrer une chaîne de 4 caractères exactement
Aqui está um exemplo de execução:
![]()
2.8.5.6. Entrada 9B: entrada de uma sequência de caracteres que deve seguir um modelo
A entrada 9B mostra como impor que uma sequência inserida tenha um número de caracteres dentro de um intervalo:
<!-- linha 10B -->
<h:outputText value="#{msg['saisie9B.prompt']}"/>
<h:inputText id="saisie9B" value="#{form.saisie9B}" styleClass="saisie" required="true" requiredMessage="#{msg['data.required']}" validatorMessage="#{msg['saisie9B.error']}">
<f:validateRegex pattern="^\s*\d{2}:\d{2}\s*$"/>
</h:inputText>
<h:message for="saisie9B" styleClass="error"/>
<h:outputText value="#{form.saisie9B}"/>
Na linha 4, o validador <f:validateRegex pattern="^\s*\d{2}:\d{2}\s*$"/> exige que a string inserida corresponda ao padrão de uma expressão regular, neste caso: uma sequência de 0 ou mais espaços, 2 dígitos, o sinal :, 2 dígitos, uma sequência de 0 ou mais espaços.
O padrão form.saisie9B do componente saisie9B da linha 3 é o seguinte:
private String saisie9B;
Existem mensagens de erro padrão para esse tipo de validação:
que podem ser substituídas usando o atributo validatorMessage, como na linha 3 acima. A mensagem de chave saisie9.error é a seguinte:
saisie9B.error=La cha\u00eene saisie ne respecte pas le format hh:mm
Aqui está um exemplo de execução:
![]()
2.8.5.7. Entrada 10: escrever um método de validação específico
Resumindo: JSF permite verificar, entre os valores inseridos, a validade de números (inteiros, reais), datas, o comprimento das cadeias de caracteres e a conformidade de uma entrada com uma expressão regular. O JSF permite adicionar, aos validadores e conversores existentes, seus próprios validadores e conversores. Esse ponto não é abordado aqui, mas é possível consultar o [ref2] para aprofundar o assunto.
Apresentamos aqui outro método: aquele que consiste em validar um dado inserido por meio de um método do modelo do formulário. Veja o exemplo a seguir:
<!-- linha 11 -->
<h:outputText value="#{msg['saisie10.prompt']}"/>
<h:inputText id="saisie10" value="#{form.saisie10}" styleClass="saisie" required="true" requiredMessage="#{msg['data.required']}" validator="#{form.validateSaisie10}"/>
<h:message for="saisie10" styleClass="error"/>
<h:outputText value="#{form.saisie10}"/>
O modelo form.saisie10 associado ao componente saisie10 da linha 3 é o seguinte:
private Integer saisie10 = 0;
Deseja-se que o número inserido seja <1 ou >7. Não é possível verificar isso com os validadores padrão do JSF. Portanto, escreve-se um método de validação próprio para o componente saisie10. Isso é indicado com o atributo validator do componente a ser validado:
<h:inputText id="saisie10" value="#{form.saisie10}" styleClass="saisie" required="true" requiredMessage="#{msg['data.required']}" validator="#{form.validateSaisie10}"/>
O componente saisie10 é validado pelo método form.validateSaisie10. Este método é o seguinte:
public void validateSaisie10(FacesContext context, UIComponent component, Object value) {
int saisie = (Integer) value;
if (!(saisie < 1 || saisie > 7)) {
FacesMessage message = Messages.getMessage(null, "saisie10.incorrecte", null);
message.setSeverity(FacesMessage.SEVERITY_ERROR);
throw new ValidatorException(message);
}
}
A assinatura de um método de validação deve ser obrigatoriamente a da linha 1:
- FacesContext context: contexto de execução da página — fornece acesso a diversas informações, notadamente aos objetos HttpServletRequest request e HttpServletResponse response,
- UIComponent component: o componente a ser validado. A tag <h:inputText> é representada por um componente do tipo UIInput derivado de UIComponent. Aqui, é esse componente UIInput que é recebido como segundo parâmetro,
- Valor do objeto: o valor inserido a ser verificado, convertido para o tipo do modelo. É importante entender aqui que, se a conversão de String para o tipo do modelo falhar, o método de validação não será executado. Quando se chega ao método validateSaisie10, significa que a conversão de String para Integer foi bem-sucedida. O terceiro parâmetro é, então, do tipo Integer.
- linha 2: o valor inserido é convertido para o tipo int,
- linha 3: verifica-se se o valor inserido é <1 ou >7. Se for esse o caso, a validação está concluída. Caso contrário, o validador deve sinalizar o erro lançando uma exceção do tipo ValidatorException.
A classe ValidatorException possui dois construtores:
![]() |
- o construtor [1] tem como parâmetro uma mensagem de erro do tipo FacesMessage. Esse tipo de mensagem é o exibido pelas tags <h:messages> e <h:message>,
- o construtor [2] permite, além disso, encapsular a causa do tipo Throwable ou derivada do erro.
Precisamos construir uma mensagem do tipo FacesMessage. Essa classe possui diversos construtores:
![]() |
O construtor [1] define as propriedades de um objeto FacesMessage:
- FacesMessage.Severity severity: um nível de gravidade selecionado da seguinte enumeração: SEVERITY_ERROR, SEVERITY_FATAL, SEVERITY_INFO, SEVERITY_WARN,
- String summary: a versão resumida da mensagem de erro — é exibida pelas tags <h:message showSummary="true"> e <h:messages>,
- String detail: a versão detalhada da mensagem de erro — é exibida pelas tags <h:message> e <h:messages showDetail="true">.
Qualquer um dos construtores pode ser utilizado, sendo que os parâmetros ausentes podem ser definidos posteriormente por meio dos métodos set.
O construtor [1] não permite especificar uma mensagem que esteja em um arquivo de mensagens internacionalizado. Isso é, obviamente, uma pena. David Geary e Cay Horstmann, no livro “Core JavaServer Faces”, preenchem essa lacuna com a classe utilitária com.corejsf.util.Messages. É essa classe que é utilizada na linha 4 do código Java para criar a mensagem de erro. Ela contém apenas métodos estáticos, incluindo o método getMessage utilizado na linha 4:
public static FacesMessage getMessage(String bundleName, String resourceId, Object[] params)
O método getMessage aceita três parâmetros:
- String bundleName: o nome de um arquivo de mensagens sem a extensão .properties, mas com o nome do pacote. Aqui, nosso primeiro parâmetro poderia ser messages para indicar o arquivo [messages.properties]. Antes de utilizar o arquivo indicado pelo primeiro parâmetro, o getMessage tenta utilizar o arquivo de mensagens do aplicativo, caso exista. Assim, se no [faces-config.xml] tiver sido declarado um arquivo de mensagens com a tag:
<application>
...
<message-bundle>messages</message-bundle>
</application>
é possível passar null como primeiro parâmetro para o método getMessage. Foi isso que foi feito aqui (ver [web.xm], página 120),
- String resourceId: a chave da mensagem a ser processada no arquivo de mensagens. Vimos que uma mensagem pode ter tanto uma versão resumida quanto uma versão detalhada. resourceId é o identificador da versão resumida. A versão detalhada será buscada automaticamente com a chave resourceId_detail. Assim, teremos duas mensagens em [messages.properties] para o erro na entrada nº 10:
saisie10.incorrecte=10-Saisie n° 10 incorrecte
saisie10.incorrecte_detail=10-Vous devez entrer un nombre entier <1 ou >7
A mensagem do tipo FacesMessage gerada pelo método Messages.getMessage inclui tanto a versão resumida quanto a detalhada, caso tenham sido encontradas. Ambas as versões devem estar presentes; caso contrário, ocorre uma exceção do tipo [NullPointerException],
- Object[] params: os parâmetros efetivos da mensagem, caso ela possua parâmetros formais {0}, {1}, ... Esses parâmetros formais serão substituídos pelos elementos da matriz params.
Voltemos ao código do método de validação do componente saisie10:
public void validateSaisie10(FacesContext context, UIComponent component, Object value) {
int saisie = (Integer) value;
if (!(saisie < 1 || saisie > 7)) {
FacesMessage message = Messages.getMessage(null, "saisie10.incorrecte", null);
message.setSeverity(FacesMessage.SEVERITY_ERROR);
throw new ValidatorException(message);
}
}
- no [4], a mensagem do tipo FacesMessage é criada por meio do método estático Messages.getMessage,
- em [5], define-se o nível de gravidade da mensagem,
- em [6], lança-se uma exceção do tipo ValidatorException com a mensagem criada anteriormente. O método de validação foi chamado pelo código XHTML a seguir:
<!-- linha 11 -->
<h:outputText value="#{msg['saisie10.prompt']}"/>
<h:inputText id="saisie10" value="#{form.saisie10}" styleClass="saisie" required="true" requiredMessage="#{msg['data.required']}" validator="#{form.validateSaisie10}"/>
<h:message for="saisie10" styleClass="error"/>
<h:outputText value="#{form.saisie10}"/>
Na linha 3, o método de validação é executado para o componente com id saisie10. Assim, a mensagem de erro gerada pelo método validateSaisie10 é associada a esse componente e, portanto, exibida na linha 4 (atributo for="saisie10"). É a versão detalhada que é exibida por padrão pela tag <h:message>.
Veja um exemplo de execução:
![]()
2.8.5.8. Entradas 11 e 12: validação de um grupo de componentes
Até agora, os métodos de validação abordados validavam apenas um único componente. Como proceder se a validação desejada envolver vários componentes? É isso que veremos agora. No formulário:

queremos que os campos 11 e 12 sejam dois números inteiros cuja soma seja igual a 10.
O código JSF será o seguinte:
<!-- linha 12 -->
<h:outputText value="#{msg['saisie11.prompt']}"/>
<h:inputText id="saisie11" value="#{form.saisie11}" styleClass="saisie" required="true" requiredMessage="#{msg['data.required']}" converterMessage="#{msg['integer.required']}"/>
<h:panelGroup>
<h:message for="saisie11" styleClass="error"/>
<h:outputText value="#{form.errorSaisie11}" styleClass="error"/>
</h:panelGroup>
<h:outputText value="#{form.saisie11}"/>
<!-- linha 13 -->
<h:outputText value="#{msg['saisie12.prompt']}"/>
<h:inputText id="saisie12" value="#{form.saisie12}" styleClass="saisie" required="true" requiredMessage="#{msg['data.required']}" converterMessage="#{msg['integer.required']}"/>
<h:panelGroup>
<h:message for="saisie12" styleClass="error"/>
<h:outputText value="#{form.errorSaisie12}" styleClass="error"/>
</h:panelGroup>
<h:outputText value="#{form.saisie12}"/>
e o modelo associado:
private Integer saisie11 = 0;
private Integer saisie12 = 0;
private String errorSaisie11 = "";
private String errorSaisie12 = "";
Na linha 3 do código JSF, utilizamos as técnicas já apresentadas para verificar se o valor inserido para o componente saisie11 é, de fato, um número inteiro. O mesmo se aplica, na linha 11, ao componente saisie12. Para verificar se saisie11 + saisie12 = 10, seria possível criar um validador específico. Essa é a solução preferível. Mais uma vez, consultaremos [ref2] para descobri-la. Aqui, seguimos uma abordagem diferente.
A página [index.xhtml] é validada por um botão [Valider], cujo código JSF é o seguinte:
<!-- botões de comando -->
<h:panelGrid columns="2">
<h:commandButton value="#{msg['submit']}" action="#{form.submit}"/>
...
</h:panelGrid>
onde a mensagem msg['submit'] é a seguinte:
submit=Valider
Vemos na linha 3 que o método form.submit será executado para processar o clique no botão [Valider]. Esse método é o seguinte:
// ações
public String submit() {
// últimas validações
validateForm();
// envia-se o mesmo formulário
return null;
}
// validações globais
private void validateForm() {
if ((saisie11 + saisie12) != 10) {
...
}
É importante entender que, quando o método submit é executado:
- todos os validadores e conversores do formulário já foram executados e concluídos com sucesso,
- os campos do modelo [Form.java] receberam os valores enviados pelo cliente.
De fato, voltemos ao ciclo de processamento de um POST JSF:
![]() |
O método submit é um gerenciador de eventos. Ele gerencia o evento clic no botão [Valider]. Como todos os gerenciadores de eventos, ele é executado na fase [E], após a execução bem-sucedida de todos os validadores e conversores ([C]) e a atualização do modelo com os valores enviados ([D]). Portanto, não se trata mais aqui de lançar exceções do tipo [ValidatorException], como fizemos anteriormente. Limitar-nos-emos a reenviar o formulário com mensagens de erro:
![]() |
Em [1], alertaremos o usuário e, em [2] e [3], colocaremos um símbolo de erro. No código JSF, a mensagem [1] será obtida da seguinte maneira:
<h:form id="formulaire">
<h:messages globalOnly="true" />
<h:panelGrid columns="4" columnClasses="col1,col2,col3,col4" border="1">
<!-- linha 1 -->
...
Na linha 2, a tag <h:messages> exibe, por padrão, a versão resumida das mensagens de erro de todas as entradas incorretas dos componentes do formulário, bem como todas as mensagens de erro não relacionadas a componentes. O atributo globalOnly="true" limita a exibição a estas últimas.
As mensagens [2] e [3] são exibidas com simples tags <h:outputText>:
<!-- linha 12 -->
<h:outputText value="#{msg['saisie11.prompt']}"/>
<h:inputText id="saisie11" value="#{form.saisie11}" styleClass="saisie" required="true" requiredMessage="#{msg['data.required']}" converterMessage="#{msg['integer.required']}"/>
<h:panelGroup>
<h:message for="saisie11" styleClass="error"/>
<h:outputText value="#{form.errorSaisie11}" styleClass="error"/>
</h:panelGroup>
<h:outputText value="#{form.saisie11}"/>
<!-- linha 13 -->
...
<h:outputText value="#{form.errorSaisie12}" styleClass="error"/>
...
Linhas 4-7: o componente saisie11 apresenta duas possíveis mensagens de erro:
- aquela que indica uma conversão incorreta ou a ausência de dados. Essa mensagem, gerada pelo próprio JSF, estará contida em um tipo FacesMessage e será exibida pela tag <h:message> da linha 5,
- aquela que vamos gerar se entrada11 + entrada12 não for igual a 10. Ela será exibida pela linha 6. A mensagem de erro estará contida no modelo form.errorSaisie11.
As duas mensagens correspondem a erros que não podem ocorrer ao mesmo tempo. A verificação “entrada11 + entrada12 = 10” é feita no método submit, que só é executado se não houver mais nenhum erro no formulário. Quando ele for executado, o componente saisie11 já terá sido verificado e seu modelo form.saise11 terá recebido seu valor. A mensagem da linha 5 não poderá mais ser exibida. Por outro lado, se a mensagem da linha 5 for exibida, significa que ainda há pelo menos um erro no formulário e o método submit não será executado. A mensagem da linha 6 não será exibida. Para que as duas possíveis mensagens de erro fiquem na mesma coluna da tabela, elas foram agrupadas em uma tag <h:panelGroup> (linhas 4 e 7).
O método submit é o seguinte:
// ações
public String submit() {
// últimas validações
validateForm();
// envia-se o mesmo formulário
return null;
}
// validações globais
private void validateForm() {
if ((saisie11 + saisie12) != 10) {
// mensagem global
FacesMessage message = Messages.getMessage(null, "saisies11et12.incorrectes", null);
message.setSeverity(FacesMessage.SEVERITY_ERROR);
FacesContext context = FacesContext.getCurrentInstance();
context.addMessage(null, message);
// mensagens relacionadas aos campos
message = Messages.getMessage(null, "error.sign", null);
setErrorSaisie11(message.getSummary());
setErrorSaisie12(message.getSummary());
} else {
setErrorSaisie11("");
setErrorSaisie12("");
}
}
- linha 4: o método submit chama o método validateForm para realizar as últimas validações,
- linha 11: verifica-se se saisie11+saisie12 = 10;
- se não for o caso, nas linhas 13-14, cria-se uma mensagem do tipo FacesMessage com a mensagem de ID saisies11et12.incorrectes. Esta é a seguinte:
saisies11et12.incorrectes=La propriété saisie11+saisie12=10 n'est pas vérifiée
- a mensagem assim criada é adicionada (linhas 15-16) à lista de mensagens de erro do aplicativo. Essa mensagem não está vinculada a um componente específico. Trata-se de uma mensagem global do aplicativo. Ela será exibida pela tag <h:messages globalOnly="true"/> apresentada acima,
- linha 18: cria-se uma nova mensagem do tipo FacesMessage com o ID de mensagem error.sign. Ela é a seguinte:
error.sign="!"
Já mencionamos que o método estático [Messages.getMessage] constrói uma mensagem do tipo FacesMessage com uma versão resumida e uma versão detalhada, caso existam. Aqui, existe apenas a versão resumida da mensagem error.sign. A versão resumida de uma mensagem m é obtida por meio de m.getSummary(). Nas linhas 19 e 20, a versão resumida da mensagem error.sign é inserida nos campos errorSaisie11 e errorSaisie12 do modelo. Eles serão exibidos pelas seguintes tags JSF:
<h:outputText value="#{form.saisie11}"/>
...
<h:outputText value="#{form.saisie12}"/>
- linhas 22-23: se a propriedade saisie11+saisie12=10 for verificada, os dois campos errorSaisie11 e errorSaisie12 do modelo serão esvaziados para que uma eventual mensagem de erro anterior seja apagada. É importante lembrar que o modelo é mantido entre as solicitações, na sessão do cliente.
Veja um exemplo de execução:
![]() |
Observe-se na coluna [1] que o modelo recebeu os valores enviados, o que demonstra que todas as operações de validação e conversão entre os valores enviados e o modelo foram bem-sucedidas. O gerenciador de eventos form.submit, que lida com o clique no botão [Valider], pôde, assim, ser executado. Foi ele que gerou as mensagens exibidas em [2] e [3]. Observa-se que o modelo foi atualizado mesmo que o formulário tenha sido rejeitado e devolvido ao cliente. Pode-se desejar que o modelo não seja atualizado nesse caso. De fato, supondo que o usuário cancele a atualização com o botão [Annuler] [4], não será possível retornar ao modelo inicial, a menos que ele tenha sido salvo.
2.8.5.9. POST de um formulário sem verificação das entradas
Consideremos o formulário acima e suponhamos que o usuário, sem perceber seus erros, queira desistir de preencher o formulário. Ele utilizará, então, o botão [Annuler] gerado pelo código JSF a seguir:
<!-- botões de comando -->
<h:panelGrid columns="2">
<h:commandButton value="#{msg['submit']}" action="#{form.submit}"/>
<h:commandButton value="#{msg['cancel']}" immediate="true" action="#{form.cancel}"/>
</h:panelGrid>
Na linha 4, a mensagem msg['cancel'] é a seguinte:
cancel=Annuler
O método form.cancel associado ao botão [Annuler] só será executado se o formulário for válido. Foi isso que demonstramos para o método form.submit associado ao botão [Valider]. Se o usuário quiser cancelar o preenchimento do formulário, é claro que não faz sentido verificar a validade dos dados inseridos. Esse resultado é obtido com o atributo immediate="true", que instrui o JSF a executar o método form.cancel sem passar pela fase de validação e conversão. Voltemos ao ciclo de processamento do POST JSF:
![]() |
Os eventos dos componentes de ação <h:commandButton> e <h:commandLink> com o atributo immediate="true" são processados na fase [C]; em seguida, o ciclo JSF passa diretamente para a fase [E] de renderização da resposta.
O método form.cancel é o seguinte:
public String cancel() {
saisie1 = 0;
saisie2 = 0;
saisie3 = 0;
saisie4 = 0;
saisie5 = 0.0;
saisie6 = 0.0;
saisie7 = true;
saisie8 = new Date();
saisie9 = "";
saisie10 = 0;
return null;
}
Se utilizarmos o botão [Annuler] no formulário anterior, obtemos como resultado a seguinte página:
![]() |
- obtém-se novamente o formulário, pois o gerenciador de eventos form.cancel retorna a chave de navegação null. A página [index.xhtml] é, portanto, retornada,
- o modelo [Form.java] foi modificado pelo método form.cancel. Isso é refletido pela coluna [2], que exibe esse modelo,
- já a coluna [3] reflete o valor lançado para os componentes.
Voltemos ao código JSF do componente saisie1 [4];
<!-- linha 1 -->
<h:outputText value="#{msg['saisie1.prompt']}"/>
<h:inputText id="saisie1" value="#{form.saisie1}" styleClass="saisie"/>
<h:message for="saisie1" styleClass="error"/>
<h:outputText value="#{form.saisie1}"/>
Na linha 4, o valor do componente saisie1 está vinculado ao modelo form.saisie1. Isso acarreta várias consequências:
- durante um GET de [index.xhtml], o componente saisie1 exibirá o valor do modelo form.saisie1,
- durante um POST a partir de [index.xhtml], o valor inserido para o componente saisie1 só é atribuído ao modelo form.saisie1 se todas as validações e conversões do formulário forem bem-sucedidas. Independentemente de o modelo ter sido atualizado ou não pelos valores lançados, se o formulário for reenviado ao final do POST, os componentes exibem o valor que foi lançado e não o valor do modelo a eles associado. É o que mostra a captura de tela acima, na qual as colunas [2] e [3] não apresentam os mesmos valores.
2.9. Exemplo mv-jsf2-07: eventos relacionados à mudança de estado dos componentes JSF
2.9.1. A aplicação
A aplicação mostra um exemplo de POST criado sem o uso de um botão ou link. O formulário é o seguinte:
![]() |
O conteúdo da lista combo2 [2] está vinculado ao elemento selecionado no combo1 [1]. Quando se altera a seleção em [1], é executado um POST do formulário, durante o qual o conteúdo de combo2 é alterado para refletir o elemento selecionado em [1]; em seguida, o formulário é devolvido. Durante esse POST, não é realizada nenhuma validação.
2.9.2. O projeto NetBeans
O projeto NetBeans do aplicativo é o seguinte:
![]() |
Temos um único formulário [index.xhtml] com seu modelo [Form.java].
2.9.3. O ambiente da aplicação
O arquivo de mensagens [messages_fr.properties]:
app.titre=intro-07
app.titre2=JSF - Listeners
combo1.prompt=combo1
combo2.prompt=combo2
saisie1.prompt=Nombre entier de type int
submit=Valider
raz=Raz
data.required=Donnée requise
integer.required=Entrez un nombre entier
saisie.type=Type de la saisie
saisie.champ=Champ de saisie
saisie.erreur=Erreur de saisie
bean.valeur=Valeurs du modèle du formulaire
A folha de estilo [styles.css]:
.info{
font-family: Arial,Helvetica,sans-serif;
font-size: 14px;
font-weight: bold
}
.col1{
background-color: #ccccff
}
.col2{
background-color: #ffcccc
}
.col3{
background-color: #ffcc66
}
.col4{
background-color: #ccffcc
}
.error{
color: #ff0000
}
.saisie{
background-color: #ffcccc;
border-color: #000000;
border-width: 5px;
color: #cc0033;
font-family: cursive;
font-size: 16px
}
.combo{
color: green;
}
.entete{
font-family: 'Times New Roman',Times,serif;
font-size: 14px;
font-weight: bold
}
2.9.4. O formulário [index.xhtml]
O formulário [index.xhtml] é o seguinte:
<?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://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<h:head>
<title>JSF</title>
<h:outputStylesheet library="css" name="styles.css"/>
...
</h:head>
<h:body style="background-image: url('${request.contextPath}/resources/images/standard.jpg');">
<h2><h:outputText value="#{msg['app.titre2']}"/></h2>
<h:form id="formulaire">
<h:messages globalOnly="true"/>
<h:panelGrid columns="4" border="1" columnClasses="col1,col2,col3,col4">
<!-- cabeçalhos -->
<h:outputText value="#{msg['saisie.type']}" styleClass="entete"/>
<h:outputText value="#{msg['saisie.champ']}" styleClass="entete"/>
<h:outputText value="#{msg['saisie.erreur']}" styleClass="entete"/>
<h:outputText value="#{msg['bean.valeur']}" styleClass="entete"/>
<!-- linha 1 -->
<h:outputText value="#{msg['combo1.prompt']}"/>
<h:selectOneMenu id="combo1" value="#{form.combo1}" immediate="true" onchange="submit();" valueChangeListener="#{form.combo1ChangeListener}" styleClass="combo">
<f:selectItems value="#{form.combo1Items}"/>
</h:selectOneMenu>
<h:panelGroup></h:panelGroup>
<h:outputText value="#{form.combo1}"/>
<!-- linha 2 -->
<h:outputText value="#{msg['combo2.prompt']}"/>
<h:selectOneMenu id="combo2" value="#{form.combo2}" styleClass="combo">
<f:selectItems value="#{form.combo2Items}"/>
</h:selectOneMenu>
<h:panelGroup></h:panelGroup>
<h:outputText value="#{form.combo2}"/>
<!-- linha 3 -->
<h:outputText value="#{msg['saisie1.prompt']}"/>
<h:inputText id="saisie1" value="#{form.saisie1}" required="true" requiredMessage="#{msg['data.required']}" styleClass="saisie" converterMessage="#{msg['integer.required']}"/>
<h:message for="saisie1" styleClass="error"/>
<h:outputText value="#{form.saisie1}"/>
</h:panelGrid>
<!-- botões de comando -->
<h:panelGrid columns="2" border="0">
<h:commandButton value="#{msg['submit']}"/>
...
</h:panelGrid>
</h:form>
</h:body>
</html>
A novidade está no código da lista combo1, linhas 24-26. Surgem novos atributos:
- onchange: atributo HTML — declara uma função ou código JavaScript que deve ser executado quando o elemento selecionado em combo1 for alterado. Aqui, o código JavaScript submit() envia o formulário para o servidor,
- valueChangeListener: atributo JSF — declara o nome do método a ser executado no lado do servidor quando o elemento selecionado em combo1 for alterado. No total, há dois métodos executados: um no lado do cliente e outro no lado do servidor,
- immediate=true: atributo JSF — define o momento em que o manipulador de eventos do lado do servidor deve ser executado: após o formulário ter sido reconstruído de acordo com as entradas do usuário, mas antes das verificações de validade das entradas. O objetivo aqui é preencher a lista combo2 de acordo com o elemento selecionado na lista combo1, mesmo que, por outro lado, haja entradas incorretas no formulário. Veja um exemplo:
![]() |
- na [1], uma primeira entrada,
- em [2], altera-se o elemento selecionado de combo1 de A para B.
O resultado obtido é o seguinte:
![]() |
O POST foi executado. O conteúdo de combo2 e [2] foi adaptado aoelemento selecionado em combo1 e [1], embora a entrada em [3] estivesse incorreta. Foi o atributo immediate=true que fez com que o método form.combo1ChangeListener fosse executado antes das verificações de validade. Sem esse atributo, ele não teria sido executado, pois o ciclo de processamento teria sido interrompido nas verificações de validade devido ao erro em [3].
As mensagens associadas ao formulário são as seguintes no [messages.properties]:
app.titre=intro-07
app.titre2=JSF - Listeners
combo1.prompt=combo1
combo2.prompt=combo2
saisie1.prompt=Nombre entier de type int
submit=Valider
raz=Raz
data.required=Donnée requise
integer.required=Entrez un nombre entier
saisie.type=Type de la saisie
saisie.champ=Champ de saisie
saisie.erreur=Erreur de saisie
bean.valeur=Valeurs du modèle du formulaire
O tempo de vida de [Form.java] está definido como “request”:
package forms;
...
@ManagedBean
@RequestScoped
public class Form {
Na linha 6, define-se o escopo do bean como “request”.
2.9.5. O modelo [Form.java]
O modelo [Form.java] é o seguinte:
package forms;
import java.util.logging.Logger;
import javax.enterprise.context.RequestScoped;
import javax.faces.bean.ManagedBean;
import javax.faces.context.FacesContext;
import javax.faces.event.ValueChangeEvent;
import javax.faces.model.SelectItem;
@ManagedBean
@RequestScoped
public class Form {
public Form() {
}
// campos do formulário
private String combo1="A";
private String combo2="A1";
private Integer saisie1=0;
// campos de trabalho
final private String[] combo1Labels={"A","B","C"};
private String combo1Label="A";
private static final Logger logger=Logger.getLogger("forms.Form");
// métodos
public SelectItem[] getCombo1Items(){
// inicializar combo1
SelectItem[] combo1Items=new SelectItem[combo1Labels.length];
for(int i=0;i<combo1Labels.length;i++){
combo1Items[i]=new SelectItem(combo1Labels[i],combo1Labels[i]);
}
return combo1Items;
}
public SelectItem[] getCombo2Items(){
// inicialização do combo2 com base no combo1
SelectItem[] combo2Items=new SelectItem[5];
for(int i=1;i<=combo2Items.length;i++){
combo2Items[i-1]=new SelectItem(combo1Label+i,combo1Label+i);
}
return combo2Items;
}
// ouvintes
public void combo1ChangeListener(ValueChangeEvent event){
// acompanhamento
logger.info("combo1ChangeListener");
// recuperamos o valor enviado por combo1
combo1Label=(String)event.getNewValue();
// retornamos a resposta, pois queremos ignorar as validações
FacesContext.getCurrentInstance().renderResponse();
}
public String raz(){
// continuação
logger.info("raz");
// zerar o formulário
combo1Label="A";
combo1="A";
combo2="A1";
saisie1=0;
return null;
}
// getters - setters
...
}
Vamos vincular o formulário [index.xhtml] ao seu modelo [Form.java]:
A lista combo1 é gerada pelo código JSF a seguir:
<h:selectOneMenu id="combo1" value="#{form.combo1}" immediate="true" onchange="submit();" valueChangeListener="#{form.combo1ChangeListener}" styleClass="combo">
<f:selectItems value="#{form.combo1Items}"/>
</h:selectOneMenu>
Ela obtém seus elementos por meio do método getCombo1Items de seu modelo (linha 2). Esse método está definido nas linhas 28 a 35 do código Java. Ele gera uma lista de três elementos {"A", "B", "C"}.
A lista combo2 é gerada pelo código JSF a seguir:
<h:selectOneMenu id="combo2" value="#{form.combo2}" styleClass="combo">
<f:selectItems value="#{form.combo2Items}"/>
</h:selectOneMenu>
Ela obtém seus elementos por meio do método getCombo2Items de seu modelo (linha 2). Esse método está definido nas linhas 37 a 44 do código Java. Ele gera uma lista de cinco elementos {"X1", "X2", "X3", "X4", "X5"}, em que X é o elemento combo1Label da linha 16. Portanto, durante a geração inicial do formulário, a lista combo2 contém os elementos {"A1","A2","A3", "A4", "A5"}.
Quando o usuário alterar o elemento selecionado na lista combo1,
- o evento onchange="submit();" será processado pelo navegador do cliente. O formulário será, portanto, enviado ao servidor;
- no lado do servidor, JSF detectará que o componente combo1 mudou de valor. O método combo1ChangeListener das linhas 47 a 54 será executado. Um método do tipo ValueChangeListener recebe como parâmetro um objeto do tipo javax.faces.event.ValueChangeEvent. Esse objeto permite obter o valor antigo e o novo do componente cujo valor foi alterado por meio dos seguintes métodos:

Aqui, o componente é a lista combo1 do tipo UISelectOne. Seu valor é do tipo String.
- linha 51 do modelo Java: o novo valor de combo1 é armazenado em combo1Label, que serve para gerar os elementos da lista combo2,
- linha 53: a resposta é retornada. É importante lembrar aqui que o gerenciador combo1ChangeListener é executado com o atributo immediate="true". Portanto, ele é executado após a fase em que a árvore de componentes da página foi atualizada com os valores enviados e antes do processo de validação desses valores. No entanto, queremos evitar esse processo de validação, pois a lista combo2 deve ser atualizada mesmo que, no formulário, ainda haja entradas incorretas. Solicitamos, portanto, que a resposta seja enviada imediatamente, sem passar pela fase de validação das entradas.
- O formulário será reenviado exatamente como foi preenchido. No entanto, os elementos das listas combo1 e combo2 não são valores lançados. Eles serão gerados novamente por meio da chamada aos métodos getCombo1Items e getCombo2Items. Este último método utilizará, então, o novo valor de combo1Label definido por combo1ChangeListener, e os elementos da lista combo2 serão alterados.
2.9.6. O botão [Raz]
Com o botão [Raz], queremos restaurar o formulário ao seu estado inicial, conforme mostrado abaixo:
![]() |
![]() |
![]() |
Em [1], o formulário antes do POST do botão [Raz]; em [2], o resultado do POST.
Embora seja funcionalmente simples, o gerenciamento desse caso de uso revela-se bastante complexo. É possível tentar diversas soluções, notadamente a utilizada para o botão [Annuler] do exemplo anterior:
<h:commandButton value="#{msg['raz']}" immediate="true" action="#{form.raz}"/>
onde o método form.raz é o seguinte:
public String raz(){
// zerar o formulário
combo1Label="A";
combo1="A";
combo2="A1";
saisie1=0;
return null;
}
O resultado obtido pelo botão [Raz] no exemplo anterior é, então, o seguinte:
![]() |
A coluna [1] mostra que o método form.raz foi executado. No entanto, a coluna [1] continua exibindo os valores lançados:
- para combo1, o valor lançado era “B”. Esse elemento é, portanto, selecionado na lista;
- para combo2, o valor lançado era “B5”. Devido à execução de form.raz, os elementos {“B1”, ..., “B5”} de combo2 foram alterados para {“A1”, ..., “A5”}. O elemento “B5” não existe mais e, portanto, não pode ser selecionado. Assim, o primeiro elemento da lista é exibido;
- para saisie1, o valor enviado foi 10.
Esse é o funcionamento normal com o atributo immediate="true". Para obter um resultado diferente, é preciso enviar os valores que se deseja ver no novo formulário, mesmo que o usuário tenha inserido outros valores. Isso é feito com um pouco de código JavaScript do lado do cliente. O formulário fica da seguinte forma:
<script language="javascript">
function raz(){
document.forms['formulaire'].elements['formulaire:combo1'].value="A";
document.forms['formulaire'].elements['formulaire:combo2'].value="A1";
document.forms['formulaire'].elements['formulaire:saisie1'].value=0;
//document.forms['formulaire'].submit();
}
</script>
...
<h:commandButton value="#{msg['raz']}" onclick='raz()' immediate="true" action="#{form.raz}"/>
- na linha 10, o atributo onclick='raz()' indica que a função JavaScript raz deve ser executada quando o usuário clicar no botão [Raz];
- linha 3: atribui-se o valor “A” ao elemento HTML com o nome 'formulário:combo1'. Os diferentes elementos da linha 3 são os seguintes:
- documento: página exibida pelo navegador,
- document.forms: conjunto de formulários do documento,
- document.forms['formulaire']: o formulário com o atributo name="formulaire",
- documents.forms['formulaire'].elements: conjunto de elementos do formulário que possuem o atributo name="formulaire",
- document.forms['formulaire'].elements['formulaire:combo1']: elemento do formulário com o atributo name="formulaire:combo1"
- document.forms['formulaire'].elements['formulaire:combo1'].value: valor que será enviado pelo elemento do formulário com o atributo name="formulaire:combo1".
Para conhecer os atributos name dos diferentes elementos da página exibida pelo navegador, é possível consultar seu código-fonte (abaixo, com IE7):
![]() |
<form id="formulaire" name="formulaire" ...>
...
<select id="formulaire:combo1" name="formulaire:combo1" ...>
Dito isso, fica claro que, no código JavaScript da função raz:
- a linha 3 faz com que o valor enviado para o componente combo1 seja a string A,
- a linha 4 faz com que o valor enviado para o componente combo2 seja a sequência A1,
- a linha 5 faz com que o valor enviado para o componente saisie1 seja a sequência 0.
Feito isso, o POST do formulário, associado a qualquer botão do tipo <h:commandButton> (linha 10), será acionado. O método form.raz será executado e o formulário será retornado exatamente como foi enviado. Obtém-se, então, o seguinte resultado:
![]() |
Esse resultado esconde muitas coisas. Os valores “A”, “A1” e “0” dos componentes combo1, combo2 e saisie1 são enviados ao servidor. Suponhamos que o valor anterior de combo1 fosse “B”. Nesse caso, há uma alteração no valor do componente combo1, e o método form.combo1ChangeListener também deveria ser executado. Temos dois manipuladores de eventos com o atributo immediate="true". Ambos serão executados? Se sim, em que ordem? Apenas um deles? Se sim, qual?
Para saber mais, criamos registros de log no aplicativo:
package forms;
import java.util.logging.Logger;
...
public class Form {
...
// campos do formulário
private String combo1="A";
private String combo2="A1";
private Integer saisie1=0;
// campos de trabalho
final private String[] combo1Labels={"A","B","C"};
private String combo1Label="A";
private static final Logger logger=Logger.getLogger("forms.Form");
// ouvinte
public void combo1ChangeListener(ValueChangeEvent event){
// acompanhamento
logger.info("combo1ChangeListener");
// recuperamos o valor enviado do combo1
combo1Label=(String)event.getNewValue();
// retornamos a resposta porque queremos ignorar as validações
FacesContext.getCurrentInstance().renderResponse();
}
public String raz(){
// continuação
logger.info("raz");
// zerar o formulário
combo1Label="A";
combo1="A";
combo2="A1";
saisie1=0;
return null;
}
...
}
- linha 16: é criado um gerador de logs. O parâmetro de getLogger permite diferenciar as origens dos logs. Aqui, o gerador de logs se chama forms.Form,
- linha 21: registramos a entrada no método combo1ChangeListener,
- linha 30: é registrado o acesso ao método raz.
Quais são os registros gerados pelo botão [Raz] ou pela alteração do valor de combo1? Consideremos diversos casos:
- utiliza-se o botão [Raz] enquanto o elemento selecionado em combo1 é “A”. “A” é, portanto, o último valor do componente combo1. Vimos que o botão [Raz] executava uma função JavaScript que enviava o valor “A” para o componente combo1. O componente combo1, portanto, não altera seu valor. Os logs mostram, então, que apenas o método form.raz é executado:
- utiliza-se o botão [Raz], embora o elemento selecionado em combo1 não seja “A”. O componente combo1, portanto, muda de valor: seu último valor não era “A” e o botão [Raz] irá atribuir a ele o valor “A”. Os logs mostram, então, que dois métodos são executados. Nesta ordem: combo1ChangeListener, raz:
![]() |
- Alteramos o valor do combo1 sem usar o botão [Raz]. Os logs mostram que apenas o método combo1ChangeListener é executado:
![]() |
2.10. Exemplo mv-jsf2-08: a tag <h:dataTable>
2.10.1. O aplicativo
A aplicação exibe uma lista de pessoas com a possibilidade de excluí-las:
![]() |
- em [1], uma lista de pessoas,
- em [2], os links que permitem excluí-las.
2.10.2. O projeto NetBeans
O projeto NetBeans do aplicativo é o seguinte:
![]() |
Temos um único formulário [index.xhtml] com seu modelo [Form.java].
2.10.3. O ambiente da aplicação
O arquivo de configuração [faces-config.xml]:
<?xml version='1.0' encoding='UTF-8'?>
<!-- =========== FULL CONFIGURATION FILE ================================== -->
<faces-config version="2.0"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd">
<application>
<resource-bundle>
<base-name>
messages
</base-name>
<var>msg</var>
</resource-bundle>
<message-bundle>messages</message-bundle>
</application>
</faces-config>
O arquivo de mensagens [messages_fr.properties]:
app.titre=intro-08
app.titre2=JSF - DataTable
submit=Valider
personnes.headers.id=Id
personnes.headers.nom=Nom
personnes.headers.prenom=Pr\u00e9nom
A folha de estilo [styles.css]:
.headers {
text-align: center;
font-style: italic;
color: Snow;
background: Teal;
}
.id {
height: 25px;
text-align: center;
background: MediumTurquoise;
}
.nom {
text-align: left;
background: PowderBlue;
}
.prenom {
width: 6em;
text-align: left;
color: Black;
background: MediumTurquoise;
}
2.10.4. O formulário [index.xhtml] e seu modelo [Form.java]
Vale lembrar a visualização associada à página [index.xhtml]:
![]() |
O formulário [index.xhtml] é o seguinte:
<?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://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<h:head>
<title>JSF</title>
<h:outputStylesheet library="css" name="styles.css"/>
</h:head>
<h:body style="background-image: url('${request.contextPath}/resources/images/standard.jpg');">
<h2><h:outputText value="#{msg['app.titre2']}"/></h2>
<h:form id="formulaire">
<h:dataTable value="#{form.personnes}" var="personne" headerClass="headers" columnClasses="id,nom,prenom">
........................
</h:dataTable>
</h:form>
</h:body>
</html>
Na linha 14, a tag <h:dataTable> utiliza o campo #{form.personnes} como fonte de dados. Ele é o seguinte:
private List<Pessoa> pessoas;
A classe [Personne] é a seguinte:
package forms;
public class Personne {
// dados
private int id;
private String nom;
private String prénom;
// fabricantes
public Personne(){
}
public Personne(int id, String nom, String prénom){
this.id=id;
this.nom=nom;
this.prénom=prénom;
}
// toString
public String toString(){
return String.format("Personne[%d,%s,%s]", id,nom,prénom);
}
// getters e setters
...
}
Voltemos ao conteúdo da tag <h:dataTable>:
<h:dataTable value="#{form.personnes}" var="personne" headerClass="headers" columnClasses="id,nom,prenom">
...
</h:dataTable>
- o atributo var="pessoa" define o nome da variável que representa a pessoa atual dentro da tag <h:datatable>,
- o atributo headerClass="headers" define o estilo dos títulos das colunas da tabela,
- o atributo columnClasses="...." define o estilo de cada uma das colunas da tabela.
Vamos examinar uma das colunas da tabela e ver como ela é construída:
![]() |
O código XHTML da coluna Id é o seguinte:
<h:dataTable value="#{form.personnes}" var="personne" headerClass="headers" columnClasses="id,nom,prenom">
<h:column>
<f:facet name="header">
<h:outputText value="#{msg['personnes.headers.id']}"/>
</f:facet>
<h:outputText value="#{personne.id}"/>
</h:column>
...
</h:dataTable>
lignes 3-5 : la balise <f:facet name="header"> définit le titre de la colonne,
ligne 4 : le titre de la colonne est pris dans le fichier des messages,
ligne 6 : personne fait référence à l'attribut var de la balise <h:dataTable ...> (ligne 1). On écrit donc l'id de la personne courante.
<h:dataTable value="#{form.personnes}" var="personne" headerClass="headers" columnClasses="id,nom,prenom">
<h:column>
<f:facet name="header">
<h:outputText value="#{msg['personnes.headers.id']}"/>
</f:facet>
<h:outputText value="#{personne.id}"/>
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="#{msg['personnes.headers.nom']}"/>
</f:facet>
<h:outputText value="#{personne.nom}"/>
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="#{msg['personnes.headers.prenom']}"/>
</f:facet>
<h:outputText value="#{personne.prénom}"/>
</h:column>
...
</h:dataTable>
- linhas 3-7: a coluna “id” da tabela,
- linhas 8-13: a coluna “sobrenome” da tabela,
- linhas 14-19: a coluna “nome” da tabela.
Agora, vamos examinar a coluna de links [Retirer]:
![]() |
Essa coluna é gerada pelo código a seguir:
<h:dataTable value="#{form.personnes}" var="personne" headerClass="headers" columnClasses="id,nom,prenom">
...
<h:column>
<h:commandLink value="Retirer" action="#{form.retirerPersonne}">
<f:setPropertyActionListener target="#{form.personneId}" value="#{personne.id}"/>
</h:commandLink>
</h:column>
</h:dataTable>
O link [Retirer] é gerado pelas linhas 4-6. Quando o link é clicado, o método [Form].retirerPersonne será executado. É hora de examinar a classe [Form.java]:
package forms;
import java.util.ArrayList;
import java.util.List;
import javax.enterprise.context.RequestScoped;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
@ManagedBean
@SessionScoped
public class Form {
// modelo
private List<Personne> personnes;
private int personneId;
// construtor
public Form() {
// inicialização da lista de pessoas
personnes = new ArrayList<Personne>();
personnes.add(new Personne(1, "dupont", "jacques"));
personnes.add(new Personne(2, "durand", "élise"));
personnes.add(new Personne(3, "martin", "jacqueline"));
}
public String retirerPersonne() {
// buscando a pessoa selecionada
int i = 0;
for (Personne personne : personnes) {
// pessoa atual = pessoa selecionada?
if (personne.getId() == personneId) {
// excluindo a pessoa atual da lista
personnes.remove(i);
// concluído
break;
} else {
// próxima pessoa
i++;
}
}
// testamos na mesma página
return null;
}
// getters e setters
...
}
- linhas 18-24: o construtor inicializa a lista de pessoas da linha 14,
- linha 10: como essa lista deve permanecer ativa ao longo das consultas, o escopo do bean é a sessão.
Quando o método [retirerPersonne] da linha 26 é executado, o campo da linha 15 foi inicializado com o ID da pessoa cujo link [Retirer] foi clicado:
<h:commandLink value="Retirer" action="#{form.retirerPersonne}">
<f:setPropertyActionListener target="#{form.personneId}" value="#{personne.id}"/>
</h:commandLink>
A tag <f:setPropertyActionListener> permite transferir informações para o modelo. Aqui, o valor do atributo value é copiado para o campo do modelo identificado pelo atributo target. Assim, o ID da pessoa atual — aquela que deve ser removida da lista de pessoas — é copiado para o campo [Form].personneId por meio do getter desse campo. Isso é feito antes da execução do método referenciado pelo atributo action da linha 1.
Linhas 26-43: o método [supprimerPersonne] exclui a pessoa cujo id é igual a personneId.
2.11. Exemplo m mv-jsf2-09: layout de uma aplicação JSF
2.11.1. A aplicação
A aplicação mostra como definir o layout de uma aplicação JSF com duas visualizações:
![]() |
A aplicação possui duas visualizações:
- em [1], a página 1,
- em [2], a página 2.
É possível navegar entre as duas páginas. O que queremos mostrar aqui é que as páginas 1 e 2 compartilham uma formatação comum, conforme pode ser visto nas capturas de tela acima.
2.11.2. O projeto NetBeans
O projeto NetBeans do aplicativo é o seguinte:
![]() |
A aplicação possui apenas páginas XHTML. Não há nenhum modelo Java associado.
2.11.3. A página [layout.xhtml]
A página [layout.xhtml] define o formato das páginas do aplicativo:
<?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://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets">
<h:head>
<title>JSF</title>
<h:outputStylesheet library="css" name="styles.css"/>
</h:head>
<h:body style="background-image: url('${request.contextPath}/resources/images/standard.jpg');">
<h:form id="formulaire">
<table style="width: 400px">
<tr>
<td colspan="2" bgcolor="#ccccff">
<ui:include src="entete.xhtml"/>
</td>
</tr>
<tr style="height: 200px">
<td bgcolor="#ffcccc">
<ui:include src="menu.xhtml"/>
</td>
<td>
<ui:insert name="contenu" >
<h2>Contenu</h2>
</ui:insert>
</td>
</tr>
<tr bgcolor="#ffcc66">
<td colspan="2">
<ui:include src="basdepage.xhtml"/>
</td>
</tr>
</table>
</h:form>
</h:body>
</html>
Na linha 7, surge um novo espaço de nomes ui. Esse espaço de nomes contém as tags que permitem formatar as páginas de uma aplicação. As tags desse espaço são utilizadas nas linhas 17, 22, 25 e 32.
A página [layout.xhtml] exibe informações em uma tabela HTML (linha 14). É possível acessar essa página com um navegador:
![]() |
- em [1], a URL solicitada.
O campo [2] foi gerado pelo código XHTML a seguir:
<h:body style="background-image: url('${request.contextPath}/resources/images/standard.jpg');">
<h:form id="formulaire">
<table style="width: 400px">
<tr>
<td colspan="2" bgcolor="#ccccff">
<ui:include src="entete.xhtml"/>
</td>
</tr>
...
</table>
</h:form>
</h:body>
A tag <ui:include> da linha 6 permite incluir na página um código XHTML externo. O arquivo [entete.xhtml] é o seguinte:
<?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://java.sun.com/jsf/html">
<body>
<h2>entête</h2>
</body>
</html>
Todo o código das linhas 3 a 8 será inserido no arquivo [layout.xhtml]. Assim, as tags <html> e <body> serão inseridas dentro de uma tag <td>. Isso não causa erros. Portanto, as páginas inseridas por <ui:include> são páginas XHTML completas. Do ponto de vista visual, apenas a linha 6 terá efeito. As tags <html> e <body> estão presentes por motivos sintáticos.
A área [3] foi gerada pelo seguinte código XHTML:
<h:form id="formulaire">
<table style="width: 400px">
<tr style="height: 200px">
<td bgcolor="#ffcccc">
<ui:include src="menu.xhtml"/>
</td>
...
</tr>
...
</table>
</h:form>
A tag <ui:include> da linha 5 inclui o seguinte arquivo [menu.xhtml]:
<?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://java.sun.com/jsf/html">
<body>
<h2>menu</h2>
</body>
</html>
A área [4] foi gerada pelo código XHTML a seguir:
<h:form id="formulaire">
<table style="width: 400px">
...
<tr bgcolor="#ffcc66">
<td colspan="2">
<ui:include src="basdepage.xhtml"/>
</td>
</tr>
</table>
</h:form>
A tag <ui:include> da linha 6 inclui o seguinte arquivo [basdepage.xhtml]:
<?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://java.sun.com/jsf/html">
<body>
<h2>bas de page</h2>
</body>
</html>
A área [5] foi gerada pelo código XHTML a seguir:
<h:form id="formulaire">
...
<td>
<ui:insert name="contenu" >
<h2>Contenu</h2>
</ui:insert>
</td>
...
</table>
</h:form>
A tag <ui:insert> da linha 5 define uma área chamada “conteúdo”. Trata-se de uma área que pode receber um conteúdo variável. Veremos como. Quando solicitamos a página [layout.xhtml], nenhum conteúdo foi definido para a área chamada “conteúdo”. Nesse caso, é utilizado o conteúdo da tag <ui:insert> das linhas 4 a 6. Portanto, a linha 5 é exibida.
2.11.4. A página [page1.xhtml]
A página [layout.xhtml] não se destina a ser visualizada. Ela serve de modelo para as páginas [page1.xhtml] e [page2.xhtml]. Trata-se de um modelo de páginas. A página [page1.xhtml] é a seguinte:
<?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://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets">
<ui:composition template="layout.xhtml">
<ui:define name="contenu">
<h2>page 1</h2>
<h:commandLink value="page 2" action="page2"/>
</ui:define>
</ui:composition>
</html>
- na linha 6, utiliza-se o espaço de nomes ui;
- na linha 7, indica-se que a página está associada ao modelo [layout.xhtml] por meio de uma tag <ui:composition>,
- na linha 8, essa associação faz com que cada tag <ui:define> seja associada a uma tag <ui:insert> do modelo utilizado, neste caso, [layout.xhtml]. A ligação é feita por meio do atributo name das duas tags. Elas devem ser idênticas.
A página exibida é [layout.xhtml], onde o conteúdo de cada tag <ui:insert> é substituído pelo conteúdo da tag <ui:define> da página solicitada. Aqui, tudo ocorre como se a página exibida fosse:
<?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://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets">
<h:head>
<title>JSF</title>
<h:outputStylesheet library="css" name="styles.css"/>
</h:head>
<h:body style="background-image: url('${request.contextPath}/resources/images/standard.jpg');">
<h:form id="formulaire">
<table style="width: 400px">
<tr>
<td colspan="2" bgcolor="#ccccff">
<ui:include src="entete.xhtml"/>
</td>
</tr>
<tr style="height: 200px">
<td bgcolor="#ffcccc">
<ui:include src="menu.xhtml"/>
</td>
<td>
<h2>page 1</h2>
<h:commandLink value="page 2" action="page2"/>
</td>
</tr>
<tr bgcolor="#ffcc66">
<td colspan="2">
<ui:include src="basdepage.xhtml"/>
</td>
</tr>
</table>
</h:form>
</h:body>
</html>
As linhas 25-26 de [page1.xhtml] foram inseridas no lugar da tag <ui:insert> de [layout.xml].
A página [page2.xhtml] é análoga à [page1.xhtml]:
<?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://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets">
<ui:composition template="layout.xhtml">
<ui:define name="contenu">
<h2>page 2</h2>
<h:commandLink value="page 1" action="page1"/>
</ui:define>
</ui:composition>
</html>
2.12. Conclusion
A análise que acabamos de fazer de JSF 2 está longe de ser exaustiva. Mas é suficiente para compreender os exemplos que se seguirão. Para aprofundar o assunto, consulte [ref2].
2.13. Testes com o Eclipse
Vamos mostrar como realizar testes em projetos Maven com o SpringSource Tool Suite:
![]() |
- No [1], importa-se um projeto Maven [2], que é selecionado com o botão [3]. Aqui, utiliza-se o projeto Maven [mv-jsf2-09] para o Eclipse
- em [4], o projeto importado foi reconhecido corretamente como um projeto Maven [5],
![]() |
- em [6]; o projeto importado para o explorador de projetos,
- em [7], ele é executado em um servidor Tomcat [8] [9],
![]() |
- em [10], o Tomcat 7 foi iniciado,
- no [11], a página inicial do projeto [mv-jsf2-09] [11] é exibida em um navegador interno do Eclipse.

























































































































































