2. Java Server Faces
Vamos agora apresentar a estrutura Java Server Faces. Será utilizada a versão 2, mas os exemplos demonstram principalmente funcionalidades da versão 1. Abordaremos apenas as funcionalidades da versão 2 que são necessárias para a aplicação de exemplo que se segue.
2.1. O papel do JSF numa aplicação Web
Primeiro, vamos situar o JSF no contexto do desenvolvimento de uma aplicação Web. Na maioria das vezes, esta será construída com base numa arquitetura multicamadas, como a seguinte:
![]() |
- A camada [web] é a camada em contacto com o utilizador da aplicação web. O utilizador interage com a aplicação web através de páginas web apresentadas por um navegador. É nesta camada que o JSF se encontra, e apenas nesta camada;
- A camada [de negócios] implementa as regras de negócio da aplicação, tais como o cálculo de um salário ou de uma fatura. Esta camada utiliza dados do utilizador através da camada [web] e do SGBD através da camada [DAO],
- a camada [DAO] (Data Access Objects), a camada [JPA] (Java Persistence API) e o controlador JDBC gerem o acesso aos dados do SGBD. A camada [JPA] funciona como um ORM (Object-Relational Mapper). Atua como uma ponte entre os objetos tratados pela camada [DAO] e as linhas e colunas de dados numa base de dados relacional;
- A integração destas camadas pode ser alcançada utilizando um contentor Spring ou EJB3 (Enterprise JavaBeans).
Os exemplos fornecidos abaixo para ilustrar o JSF utilizarão apenas uma única camada, a camada [web]:
![]() |
Assim que os conceitos básicos do JSF forem dominados, iremos construir aplicações Java EE multicamadas.
2.2. O modelo de desenvolvimento MVC do JSF
O JSF implementa o padrão arquitetónico MVC (Modelo–Visão–Controlador) da seguinte forma:
![]() |
Esta arquitetura implementa o padrão de design MVC (Modelo, Vista, Controlador). O processamento de um pedido do cliente segue estes quatro passos:
- solicitação – o navegador do cliente envia uma solicitação ao controlador [Faces Servlet]. O controlador lida com todas as solicitações do cliente. É o ponto de entrada da aplicação. Este é o C em MVC,
- processamento – o controlador C processa esta solicitação. Para tal, conta com a assistência de manipuladores de eventos específicos da aplicação [2a]. Estes manipuladores podem necessitar da assistência da camada de negócios [2b]. Uma vez processada a solicitação do cliente, ela pode desencadear várias respostas. Um exemplo clássico é:
- uma página de erro, se a solicitação não puder ser processada corretamente;
- uma página de confirmação, caso contrário,
- Navegação - O controlador seleciona a resposta (= vista) a enviar ao cliente. A seleção da resposta a enviar ao cliente envolve várias etapas:
- selecionar o Facelet que irá gerar a resposta. Isto é chamado de vista V, o V em MVC. Esta escolha depende geralmente do resultado da execução da ação solicitada pelo utilizador;
- fornecer a este Facelet os dados de que necessita para gerar esta resposta. Com efeito, esta resposta contém, na maioria das vezes, informações calculadas pelo controlador. Estas informações constituem o que se denomina o modelo M da vista, o M em MVC,
O Passo 3 consiste, portanto, em selecionar uma vista V e construir o modelo M necessário para ela.
- Resposta - o controlador C instrui o Facelet selecionado a renderizar-se. O Facelet utiliza o modelo M preparado pelo controlador C para inicializar as partes dinâmicas da resposta que deve enviar ao cliente. A forma exata desta resposta pode variar: pode ser um fluxo HTML, PDF, Excel, etc.
Num projeto JSF:
- o controlador C é o servlet [javax.faces.webapp.FacesServlet]. Este encontra-se na biblioteca [javaee.jar],
- as vistas V são implementadas por páginas que utilizam a tecnologia Facelets,
- os modelos M e os manipuladores de eventos são implementados por classes Java frequentemente chamadas de «backing beans» ou simplesmente beans.
Agora, vamos esclarecer a relação entre a arquitetura web MVC e a arquitetura em camadas. Trata-se de dois conceitos diferentes que, por vezes, são confundidos. Considere uma aplicação web JSF de camada única:
![]() |
Se implementarmos a camada [web] com JSF, teremos, de facto, uma arquitetura web MVC, mas não uma arquitetura em camadas. Aqui, a camada [web] tratará de tudo: apresentação, lógica de negócio e acesso aos dados. Com o JSF, os beans farão esse trabalho.
Agora, vamos considerar uma arquitetura web multicamadas:
![]() |
A camada [web] pode ser implementada sem um framework e sem seguir o modelo MVC. Temos, então, uma arquitetura multicamadas, mas a camada web não implementa o modelo MVC.
No MVC, dissemos que o modelo M é o da vista V, ou seja, o conjunto de dados exibidos pela vista V. É frequentemente apresentada outra definição do modelo M no MVC:
![]() |
Muitos autores consideram que o que se encontra à direita da camada [web] constitui o modelo M do MVC. Para evitar ambiguidades, referir-nos-emos a:
- o modelo de domínio quando nos referirmos a tudo o que está à direita da camada [web],
- o modelo de vista quando nos referirmos aos dados apresentados por uma vista V.
Daqui em diante, o termo «modelo M» referir-se-á exclusivamente ao modelo de uma vista V.
2.3. Exemplo mv-jsf2-01: os componentes de um projeto JSF
Os primeiros exemplos limitar-se-ão à camada web implementada com JSF 2:
![]() |
Depois de abordarmos os conceitos básicos, iremos estudar exemplos mais complexos com arquiteturas multicamadas.
2.3.1. Geração de projetos
Geramos o nosso primeiro projeto JSF2 utilizando o NetBeans 7.
![]() |
- Em [1], crie um novo projeto,
- em [2], selecione a categoria [Maven] e o tipo de projeto [Aplicação Web],
![]() |
- em [3], especifique a pasta pai para o novo projeto,
- em [4], nomeie o projeto,
- em [5], escolha um servidor. Com o NetBeans 7, pode escolher entre os servidores Apache Tomcat e GlassFish. A diferença entre os dois é que o GlassFish suporta EJBs (Enterprise Java Beans) e o Tomcat não. Os nossos exemplos JSF não utilizarão EJBs. Portanto, aqui pode escolher qualquer um dos servidores,
- em [6], selecione a versão Web Java EE 6,
- 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:
- [Páginas Web]: conterá as páginas web (.xhtml, .jsp, .html), recursos (imagens, vários documentos), a configuração da camada web e a configuração da estrutura JSF;
- [Pacotes de código-fonte]: as classes Java do projeto;
- [Dependências]: os arquivos .jar necessários ao projeto e geridos pela estrutura Maven;
- [Dependências Java]: os arquivos .jar necessários ao projeto e não geridos pela estrutura Maven;
- [Ficheiros do projeto]: ficheiros de configuração do Maven e do NetBeans,
![]() |
- em [2]: o ramo [Páginas Web],
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>
Esta é uma página web que exibe a frase «Olá, Mundo!» em letras grandes.
O ficheiro [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 o seu nome) é /mv-jsf2-01. Isto significa que as páginas web do projeto serão solicitadas através de um URL do tipo http://machine:port/mv-jsf2-01/page. O contexto é, por predefinição, o nome do projeto. Não será necessário modificar este ficheiro.
![]() |
- Em [3], o ramo [Source Packages],
Este ramo contém o código-fonte das classes Java do projeto. Aqui não temos classes. O NetBeans gerou um pacote padrão que pode ser eliminado [4].
![]() |
- em [5], o ramo [Dependencies],
Este ramo apresenta todas as bibliotecas necessárias ao projeto e geridas pelo Maven. Todas as bibliotecas aqui listadas serão automaticamente descarregadas pelo Maven. É por isso que um projeto Maven requer acesso à Internet. As bibliotecas descarregadas serão armazenadas localmente. Se outro projeto precisar de uma biblioteca que já esteja presente localmente, esta não será descarregada. Veremos que esta lista de bibliotecas, bem como os repositórios onde podem ser encontradas, estão definidos no ficheiro de configuração do projeto Maven.
![]() |
- em [6], as bibliotecas necessárias ao projeto e não geridas pelo Maven,
![]() |
- em [7], os ficheiros de configuração do projeto Maven:
- [nb-configuration.xml] é o ficheiro de configuração do NetBeans. Não nos preocuparemos com ele.
- [pom.xml]: o ficheiro de configuração do Maven. POM significa Project Object Model. Por vezes, poderemos precisar de editar este ficheiro diretamente.
O ficheiro [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–8 definem o artefacto Java que será criado pelo projeto Maven. Esta informação provém do assistente utilizado durante a criação do projeto:
![]() |
Um artefacto Maven é definido por quatro propriedades:
- [groupId]: informação semelhante a um nome de pacote. Por exemplo, as bibliotecas do framework Spring têm groupId=org.springframework, enquanto as do framework JSF têm groupId=javax.faces,
- [artifactId]: o nome do artefacto Maven. No grupo [org.springframework], encontramos os seguintes artifactIDs: spring-context, spring-core, spring-beans, ... No grupo [javax.faces], encontramos o artifactId jsf-api,
- [version]: o número da versão do artefacto Maven. Assim, o artefacto org.springframework.spring-core tem as seguintes versões: 2.5.4, 2.5.5, 2.5.6, 2.5.6.SECO1, ...
- [packaging]: o formato do artefacto, mais frequentemente war ou jar.
O 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). Estas quatro informações devem identificar de forma única um artefacto Maven.
As linhas 17–24 listam as dependências do projeto Maven, ou seja, a lista de bibliotecas necessárias ao projeto. Cada biblioteca é definida pelas quatro informações (groupId, artifactId, version, packaging). Quando a informação de packaging está em falta, como neste caso, é utilizada a embalagem jar. É adicionada outra informação: scope, que especifica em que fases do ciclo de vida do projeto a biblioteca é necessária. O valor predefinido é compile, o que indica que a biblioteca é necessária tanto para a compilação como 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, em tempo de execução, será fornecida pelo servidor Tomcat 7.
2.3.2. Executar o projeto
Executamos o projeto:
![]() |
Em [1], o projeto Maven é executado. O servidor Tomcat é então iniciado, caso ainda não o estivesse. Um navegador também é iniciado e a URL do contexto do projeto é solicitada [2]. Como nenhum documento é solicitado, a página index.html, index.jsp ou index.xhtml é utilizada, caso exista. Aqui, será a página [index.jsp].
2.3.3. O sistema de ficheiros de um projeto Maven
![]() |
- [1]: O sistema de ficheiros do projeto encontra-se no separador [Ficheiros],
- [2]: os ficheiros fonte Java encontram-se na pasta [src/main/java],
- [3]: as páginas web estão na pasta [src/main/webapp],
- [4]: A pasta [target] é criada pela compilação do projeto,
- [5]: Aqui, a compilação do projeto criou um arquivo [mv-jsf2-01-1.0-SNAPSHOT.war]. Este é o arquivo que foi executado pelo servidor Tomcat.
2.3.4. Configurar um projeto para JSF
O nosso projeto atual não é um projeto JSF. Faltam-lhe as bibliotecas da estrutura JSF. Para transformar o projeto atual num projeto JSF, proceda da seguinte forma:
![]() |
- Em [1], aceda às propriedades do projeto,
- em [2], selecione a categoria [Frameworks],
- em [3], adicione uma estrutura,
![]() |
- em [4], selecione Java Server Faces,
- Em [5], o NetBeans oferece a versão 2.1 da estrutura. Aceite-a,
- em [6], o projeto é então melhorado com novas dependências.
O ficheiro [pom.xml] foi atualizado para refletir esta 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>
Linhas 14–33: foram adicionadas novas dependências. O Maven descarrega-as automaticamente. Ele obtém-nas a partir do que se denomina repositórios. O Repositório Central é utilizado por predefinição. Podem ser adicionados repositórios adicionais utilizando a tag <repository>. Aqui, foram adicionados dois repositórios:
- 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 foi melhorado com uma nova página 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>
Aqui temos um ficheiro XML (linha 1). Contém tags HTML, mas em formato XML. Isto é chamado de XHTML. A tecnologia utilizada para criar páginas web com o JSF 2 chama-se Facelets. Por isso, a página XHTML é por vezes referida como uma página Facelet.
As linhas 3–4 definem a tag <html> com namespaces XML (xmlns=XML Name Space).
- A linha 3 define o namespace principal http://www.w3.org/1999/xhtml,
- a linha 4 define o namespace http://java.sun.com/jsf/html para tags HTML. Estas tags terão o prefixo h:, conforme indicado por xmlns:h. Estas tags podem ser encontradas nas linhas 5, 7, 8 e 10.
Quando encontra uma declaração de namespace, o servidor web irá procurar nos diretórios [META-INF] no classpath da aplicação por ficheiros com a extensão .tld (TagLib Definition). Aqui, irá encontrá-los no arquivo [jsf-impl.jar] [1,2]:
![]() |
Vamos dar uma olhada [3] no ficheiro [HTML_basic.tld]:
- na linha 19, o URI da biblioteca de tags,
- na linha 16, o seu nome abreviado.
As definições das várias tags <h:xx> encontram-se neste ficheiro. Estas tags são geridas por classes Java que também se encontram no artefacto [jsf-impl.jar].
Voltemos ao nosso projeto JSF. Este foi expandido com um novo ramo:
![]() |
O ramo [Outras fontes] [1] contém ficheiros que devem estar no classpath do projeto e que não são código Java. Isto aplica-se aos ficheiros de mensagens JSF. Vimos que, sem adicionar a estrutura JSF ao projeto, este ramo não existe. Para o criar, basta criar a pasta [src/main/resources] [3] no separador [Ficheiros] [2].
Por fim, surgiu uma nova pasta na ramificação [Páginas Web]:
![]() |
A pasta [WEB-INF] foi criada, contendo o ficheiro [ web.xml]. Este ficheiro configura a aplicação 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, ou seja, uma classe Java capaz de processar pedidos dos clientes. Uma aplicação JSF funciona da seguinte forma:
![]() |
Esta arquitetura implementa o padrão de design MVC (Modelo, Vista, Controlador). Vamos rever o que foi mencionado anteriormente. O processamento de uma solicitação do cliente envolve as seguintes quatro etapas:
1 - Pedido - o navegador do cliente envia um pedido ao controlador [Faces Servlet]. O controlador trata de todos os pedidos do cliente. É o ponto de entrada da aplicação. Este é o C em MVC,
2 - Processamento - o controlador C processa esta solicitação. Para tal, conta com a assistência de manipuladores de eventos específicos da aplicação [2a]. Estes manipuladores podem necessitar da assistência da camada de negócios [2b]. Uma vez processada a solicitação do cliente, esta pode desencadear várias respostas. Um exemplo clássico é:
- uma página de erro, se a solicitação não puder ser processada corretamente;
- uma página de confirmação, caso contrário,
3 - navegação - o controlador seleciona a resposta (= vista) a enviar ao cliente. A seleção da resposta a enviar ao cliente envolve várias etapas:
- selecionar o Facelet que irá gerar a resposta. Isto é chamado de vista V, o V em MVC. Esta escolha depende geralmente do resultado da execução da ação solicitada pelo utilizador;
- fornecer a este Facelet os dados de que necessita para gerar esta resposta. Com efeito, esta resposta contém, na maioria das vezes, informações calculadas pelo controlador. Estas informações constituem o que se denomina o modelo M da vista, o M em MVC,
O Passo 3 consiste, portanto, em selecionar uma vista V e construir o modelo M necessário para ela.
4 - Resposta - O controlador C instrui o Facelet selecionado a renderizar-se. O Facelet utiliza o modelo M preparado pelo controlador C para inicializar as partes dinâmicas da resposta que deve enviar ao cliente. O formato exato desta resposta pode variar: pode ser um fluxo HTML, PDF, Excel, etc.
Num projeto JSF:
- o controlador C é o servlet [javax.faces.webapp.FacesServlet],
- as vistas V são implementadas por páginas que utilizam a tecnologia Facelets,
- os modelos M e os manipuladores de eventos são implementados por classes Java frequentemente referidas como «backing beans» ou, mais simplesmente, Beans.
Vamos dar mais uma olhadela ao conteúdo do ficheiro [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> é utilizada para associar um servlet a um URL solicitado pelo navegador do cliente. Aqui, especifica que os URLs do formato [/faces/*] devem ser tratados pelo servlet denominado [Faces Servlet]. Este servlet é definido nas linhas 7–10. Uma vez que não existem outras tags <servlet-mapping> no ficheiro, isto significa que o [Faces Servlet] apenas tratará URLs do tipo [/faces/*]. Vimos que o contexto da aplicação se denomina [/mv-jsf2-01]. As URLs do cliente tratadas pelo [Faces Servlet] terão, portanto, o formato [http://machine:port/mv-jsf2-01/faces/*]. As páginas .html e .jsp serão tratadas por predefinição pelo próprio contentor de servlets, e não por um servlet específico. Isto porque o contentor de servlets sabe como tratá-las,
- linhas 7–10: definem o [Faces Servlet]. Uma vez que todas as URLs aceites são direcionadas para ele, é o controlador C no modelo MVC,
- linha 10: indica que o servlet deve ser carregado na memória assim que o servidor web for iniciado. Por padrão, um servlet é carregado apenas após a receção do primeiro pedido que lhe é feito,
- linhas 3–6: definem um parâmetro para o [Faces Servlet]. O parâmetro javax.faces.PROJECT_STAGE define a fase do projeto em execução. Na fase de Desenvolvimento, o [Faces Servlet] exibe mensagens de erro úteis para a depuração. Na fase de Produção, estas mensagens deixam de ser exibidas,
- linhas 17–19: duração da sessão em minutos. Um cliente interage com a aplicação através de uma série de ciclos de pedido/resposta. Cada ciclo utiliza a sua própria ligação TCP/IP, que é estabelecida de novo a cada 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 possui a memória de estado do cliente ( ). Esta é uma característica do protocolo HTTP (HyperText Transfer Protocol): o cliente comunica-se com o servidor através de uma série de ciclos de solicitação do cliente/resposta do servidor, cada um utilizando uma nova conexão TCP/IP. Isto é conhecido como um protocolo sem estado. Noutros protocolos, como o FTP (File Transfer Protocol), o cliente C utiliza a mesma ligação durante toda a sua interação com o servidor S. Uma ligação está, portanto, ligada a um cliente específico. O servidor S sabe sempre com quem está a lidar. Para reconhecer que um pedido pertence a um determinado cliente, o servidor web pode utilizar a técnica de sessão:
- Quando um cliente faz o seu primeiro pedido, o servidor S envia a resposta esperada juntamente com um token, uma sequência aleatória de caracteres exclusiva desse cliente;
- a cada pedido subsequente, o cliente C reenvia o token que recebeu ao servidor S, permitindo assim que o servidor S o reconheça.
A aplicação pode agora solicitar ao servidor que armazene informações associadas a um cliente específico. Isto é designado por sessão do cliente. A linha 18 indica que a duração de uma sessão é de 30 minutos. Isto significa que, se um cliente C não efetuar um novo pedido dentro de 30 minutos, a sua sessão é encerrada e as informações nela contidas são perdidas. No seu próximo pedido, tudo decorrerá como se fosse um novo cliente, e uma nova sessão terá início;
- Linhas 21–23: a lista de páginas a apresentar quando o utilizador solicita o contexto sem especificar uma página, por exemplo, aqui [http://machine:port/mv-jsf2-01]. Neste caso, o servidor web (não o servlet) verifica se a aplicação definiu uma tag <welcome-file-list>. Se assim for, apresenta a primeira página encontrada na lista. Se essa página não existir, exibe a segunda página e assim sucessivamente até encontrar uma página existente. Aqui, quando o cliente solicita o URL [http://machine:port/mv-jsf2-01], o URL [http://machine:port/mv-jsf2-01/index.xhtml] será servido.
2.3.5. Execute o projeto
Quando o novo projeto é executado, o resultado apresentado no navegador é o seguinte:
![]() |
- em [1], o contexto foi solicitado sem especificar um documento,
- em [2], conforme explicado, a página inicial (ficheiro de boas-vindas) [index.xhtml] é apresentada.
Talvez tenha curiosidade em ver o código-fonte recebido [3]:
Recebemos algum HTML. Todas as tags <h:xx> em index.xhtml foram traduzidas para as respetivas tags HTML.
2.3.6. O Repositório Local do Maven
Mencionámos que o Maven descarrega as dependências necessárias para o projeto e as armazena localmente. Pode explorar este repositório local:
![]() |
- Em [1], selecione a opção [Janela / Outros / Navegador de Repositórios Maven],
- em [2], abre-se o separador [Maven Repositories],
- em [3], esta contém duas ramificações, uma para o repositório local e outra para o repositório central. Este último é enorme. Para visualizar o seu conteúdo, é necessário atualizar o seu índice [4]. Esta atualização demora várias dezenas de minutos.
![]() |
- em [5], as bibliotecas no repositório local,
- em [6], encontrará um ramo [istia.st] que corresponde ao [groupId] do nosso projeto,
- em [7], pode aceder às propriedades do repositório local,
- em [8], vê o caminho para o repositório local. É útil saber isto porque, por vezes (raramente), o Maven deixa de utilizar a versão mais recente do projeto. Faz alterações e repara que estas não estão a ser aplicadas. Pode então eliminar manualmente o ramo no repositório local correspondente ao seu [groupId]. Isto obriga o Maven a recriar o ramo a partir da versão mais recente do projeto.
2.3.7. Pesquisar um artefacto com o Maven
Agora vamos aprender a pesquisar um artefacto com o Maven. Comecemos pela lista de dependências atuais no ficheiro [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–40 definem as dependências e as linhas 45–58 especificam os repositórios onde estas podem ser encontradas, para além do repositório central, que é sempre utilizado. Iremos modificar as dependências para utilizar as bibliotecas nas suas versões mais recentes.
![]() |
Primeiro, removemos as dependências atuais [1]. O ficheiro [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 já não aparecem no [pom.xml]. Agora, vamos procurá-las nos repositórios do Maven.
![]() |
- Em [1], adicionamos uma dependência ao projeto;
- em [2], temos de especificar informações sobre o artefacto que procuramos (groupId, artifactId, versão, embalagem (Tipo) e âmbito). Começamos por especificar o [groupId] [3],
- em [4], premimos [espaço] para exibir a lista de artefactos possíveis. Aqui, [jsf-api] e [jsf-impl]. Escolhemos [jsf-api],
- em [5], seguindo o mesmo procedimento, selecionamos a versão mais recente. O tipo de empacotamento é jar.
Procedemos desta forma para todos os artefactos:
![]() | ![]() | ![]() | ![]() |
![]() |
Em [6], as dependências adicionadas aparecem no projeto. O ficheiro [pom.xml] reflete estas 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>
Agora, suponhamos que não sabemos o [groupId] do artefacto que queremos. Por exemplo, queremos usar o Hibernate como um ORM (Mapeador Objeto-Relacional), e isso é tudo o que sabemos. Podemos então ir ao site [http://mvnrepository.com/]:
![]() |
Em [1], pode introduzir palavras-chave. Digite hibernate e execute a pesquisa.
![]() |
- Em [2], selecione o [groupId] org.hibernate e o [artifactId] hibernate-core,
- Em [3], selecione a versão 4.1.2-Final,
- em [4], obtemos o código Maven para colar no ficheiro [pom.xml]. Vamos fazer 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>
Guardamos o ficheiro [pom.xml]. O Maven descarrega então as 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 encontrada, este [artifactId] também é descrito por um ficheiro [pom.xml]. Este ficheiro foi lido e o Maven descobriu que o [artifactId] tinha dependências. Ele descarrega-as também. Fará isto para cada [artifactId] descarregado. Por fim, encontramos em [6] dependências que não solicitámos diretamente. Estas são indicadas por um ícone diferente do do [artifactId] principal.
Neste documento, utilizamos o Maven principalmente por esta funcionalidade. Isto evita que tenhamos de conhecer todas as dependências de uma biblioteca que pretendemos utilizar. Deixamos que o Maven as gere. Além disso, ao partilhar um ficheiro [pom.xml] entre os programadores, garantimos que todos os programadores estão, de facto, a utilizar as mesmas bibliotecas.
Nos exemplos que se seguem, iremos simplesmente fornecer o ficheiro [pom.xml] utilizado. O leitor apenas precisa de o utilizar para replicar as condições descritas no documento. Além disso, os projetos Maven são suportados pelas principais IDEs Java (Eclipse, NetBeans, IntelliJ, JDeveloper). Assim, o leitor pode utilizar a sua IDE preferida para testar os exemplos.
2.4. Exemplo mv-jsf2-02: Gestor de eventos – Internacionalização – Navegação na página
2.4.1. A aplicação
A aplicação é a seguinte:
![]() |
- [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,
- quando clica em [3], a página [4] é apresentada,
- o link [5] leva-o de volta à página inicial.
![]() |
- na página inicial [1], os links [2] permitem-lhe alterar o idioma,
- em [3], a página inicial em inglês.
2.4.2. O Projeto NetBeans
Vamos criar um novo projeto web, conforme explicado na secção 2.3.1. Vamos chamá-lo de mv-jsf2-02:
![]() |
- em [1], o projeto gerado,
- em [2], removemos o pacote [istia.st.mvjsf202] e o ficheiro [index.jsp],
- em [3], adicionámos dependências do Maven utilizando o seguinte ficheiro [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 da estrutura JSF. Basta copiar as linhas acima para o ficheiro [pom.xml] para substituir as dependências antigas.
![]() |
- Em [4, 5]: Crie uma pasta [src/main/resources] no separador [Ficheiros],
- em [6], no separador [Projects], isto criou o ramo [Other Sources].
Agora temos um projeto JSF. Vamos criar diferentes tipos de ficheiros nele:
- páginas web no formato XHTML,
- classes Java,
- ficheiros de mensagens,
- o ficheiro de configuração do projeto JSF.
Vamos ver como criar cada tipo de ficheiro:
![]() |
- Em [1], criamos uma página JSF
- em [2], criamos uma página [index.xhtml] no formato [Facelets] [3],
- em [4], foram criados dois ficheiros: [index.xhtml] e [WEB-INF/web.xml].
O ficheiro [ web.xml] configura a aplicação JSF. Tem o seguinte aspeto:
<?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á abordámos este ficheiro na secção 2.3.4. Vamos rever as suas principais propriedades:
- todas as URLs do tipo faces/* são tratadas pelo servlet [javax.faces.webapp.FacesServlet],
- a página [index.xhtml] é a página inicial da aplicação.
O ficheiro [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 deparámos com este ficheiro na Secção 2.3.4.
Agora vamos criar uma classe Java:
![]() |
- Em [1], crie uma classe Java no ramo [Source Packages],
- em [2], damos-lhe um nome e colocamo-la num pacote [3],
- em [4], a classe criada aparece no projeto.
O código da classe criada é um esqueleto 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 ficheiro de mensagens:
- em [1], crie um ficheiro [Properties],
- em [2], introduza o nome do ficheiro e, em [3], a sua pasta,
- em [4], o ficheiro [messages.properties] foi criado.
Por vezes, é necessário criar o ficheiro [WEB-INF/faces-config.xml] para configurar o projeto JSF. Este ficheiro era obrigatório no JSF 1. No JSF 2, é opcional. No entanto, é necessário se o site JSF for internacionalizado. Este será o caso mais à frente. Por isso, vamos agora mostrar-lhe como criar este ficheiro de configuração.
![]() |
- Em [1], criamos o ficheiro de configuração do JSF;
- em [2], introduzimos o seu nome e, em [3], a sua pasta,
- em [4], o ficheiro criado.
O ficheiro [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 desta tag está vazio. Teremos de o preencher mais tarde.
Agora temos todos os elementos necessários para criar um projeto JSF. Nos exemplos que se seguem, apresentamos o projeto JSF completo e, em seguida, detalhamos os seus elementos um a um. Apresentamos agora um projeto para explicar os conceitos:
- gestão de eventos de formulário,
- internacionalização de páginas num site JSF,
- navegação entre páginas.
O projeto [mv-jsf2-02] tem este aspeto. Os leitores podem encontrá-lo no site de exemplos (ver Secção 1.2).
![]() |
- em [1], os ficheiros de configuração do projeto JSF,
- em [2], as páginas JSF do projeto,
- em [3], a única classe Java,
- em [4], os ficheiros de mensagens.
2.4.3. A página [index.xhtml]
O ficheiro [index.xhtml] [1] envia a página [2] para o navegador do cliente:
![]() |
O código que gera esta 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 namespaces/bibliotecas de tags utilizados pela página. As tags com o prefixo h são tags HTML, enquanto as tags com o prefixo f são tags específicas do JSF,
- linha 10: a tag <f:view> é utilizada para delimitar o código que o motor JSF deve processar, onde aparecem as tags <f:xx>. O atributo locale permite especificar um idioma de exibição para a página. Aqui, utilizaremos dois: inglês e francês. O valor do atributo locale é expresso como uma expressão EL (Expression Language) #{expressão}. A forma da expressão pode variar. Na maioria das vezes, iremos expressá-la como bean['key'] ou bean.field. Nos nossos exemplos, bean será uma classe Java ou um ficheiro de mensagens. Com o JSF 1, estes beans tinham de ser declarados no ficheiro [faces-config.xml]. Com o JSF 2, isto já não é obrigatório para classes Java. Podemos agora utilizar anotações que transformam uma classe Java num bean reconhecido pelo JSF 2. O ficheiro de mensagens deve ser declarado no ficheiro 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 como [ChangeLocale].getLocale(). Geralmente, a expressão #{bean.field} é avaliada como [Bean].getField(), onde [Bean] é uma instância da classe Java à qual foi atribuído o nome bean, e getField é o getter associado ao campo 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{
// page locale
private String locale="fr";
public ChangeLocale() {
}
...
public String getLocale() {
return locale;
}
}
- linha 11: o campo locale,
- linha 17: o seu getter,
- linha 7: a anotação ManagedBean torna a classe Java ChangeLocale um bean reconhecido pelo JSF. Um bean é identificado por um nome. Este pode ser definido utilizando o atributo name da anotação: @ManagedBean(name="xx"). Se o atributo name for omitido, é utilizado o nome da classe com o primeiro caractere em minúscula. O nome do bean ChangeLocale é, portanto, changeLocale. Note 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 âmbito do bean. Existem vários âmbitos. Utilizaremos normalmente os três seguintes:
- RequestScoped: O tempo de vida do bean corresponde ao ciclo de pedido do navegador/resposta do servidor. Se este bean for necessário novamente para processar um novo pedido do mesmo navegador ou de outro, será instanciado novamente,
- SessionScoped: O tempo de vida do bean corresponde ao da sessão de um cliente específico. O bean é inicialmente criado para tratar uma das solicitações desse cliente. Permanece então na memória dentro da sessão desse cliente. Um bean deste tipo armazena normalmente dados específicos de um determinado cliente. Será destruído quando a sessão do cliente for encerrada,
- ApplicationScoped: A duração do bean é a da própria aplicação. Um bean com este âmbito é, na maioria das vezes, partilhado por todos os clientes da aplicação. É geralmente inicializado no arranque da aplicação.
Estas anotações existem em dois pacotes: javax.enterprise.context.SessionScoped (JSF 2) e javax.faces.bean.SessionScoped (JSF 1). Aqui, estamos a utilizar o pacote JSF 2. Isto requer que criemos o ficheiro [WEB-INF/beans.xml]:
![]() |
Este ficheiro é gerado automaticamente pelo NetBeans quando importa o pacote [javax.enterprise.context.SessionScoped]. O 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 ficheiro está vazio. Isso é suficiente. Apenas a sua presença é necessária.
Por fim, note que a classe [ChangeLocale] implementa a interface [Serializable]. Isto é necessário para beans com âmbito de sessão, que o servidor web poderá precisar de serializar em ficheiros. Voltaremos ao bean [ChangeLocale] mais tarde.
2.4.5. O ficheiro de mensagens
Voltemos ao ficheiro [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.title']} na forma #{bean['field']}. bean é o nome de uma classe Java ou de um ficheiro de mensagens. Aqui, é o nome de um ficheiro de mensagens. O ficheiro de mensagens deve ser declarado no ficheiro 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> é utilizada para configurar a aplicação JSF,
- linhas 12–17: a tag <resource-bundle> é utilizada para definir recursos para a aplicação, neste caso um ficheiro de mensagens,
- linhas 13–15: a tag <base-name> define o nome do ficheiro de mensagens,
- linha 14: o ficheiro será denominado messages[_LanguageCode][_CountryCode].properties. A tag <base-name> define apenas a primeira parte do nome. O resto é implícito. Podem existir vários ficheiros de mensagens, um por idioma:
![]() |
- em [1], vemos quatro ficheiros de mensagens correspondentes ao nome base `messages` definido em [faces-config.xml],
- messages_fr.properties: contém mensagens em francês (código fr);
- messages_en.properties: contém mensagens em inglês (código en);
- messages_es_ES.properties: contém mensagens em espanhol (código es) da Espanha (código ES). Existem outros tipos de espanhol, como o da Bolívia (es_BO);
- messages.properties: é utilizado pelo servidor quando o idioma da máquina em que está a ser executado não tem nenhum ficheiro de mensagens associado. Seria utilizado, por exemplo, se a aplicação estivesse a ser executada numa máquina na Alemanha, onde o idioma predefinido é o alemão (de). Uma vez que não existe nenhum ficheiro [messages_de.properties], a aplicação utilizaria o ficheiro [messages.properties],
- em [2]: os códigos de idioma estão sujeitos a uma norma internacional,
- em [3]: o mesmo se aplica aos códigos de país.
O nome do ficheiro de mensagens é definido na linha 14. Será procurado no Classpath do projeto. Se estiver dentro de um pacote, esse pacote deve ser definido na linha 14, por exemplo resources.messages, se o ficheiro [messages.properties] estiver localizado na pasta [resources] do Classpath. Uma vez que o nome na linha 14 não inclui um pacote, o ficheiro [messages.properties] deve ser colocado na raiz da pasta [src/main/resources]:
![]() |
Em [1], no separador [Projects] do projeto NetBeans, o ficheiro [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 [languageCode_countryCode_variantCode]. Em [1], apenas o [código de idioma] foi utilizado: en para inglês, fr para francês. Cada versão é armazenada num ficheiro separado no sistema de ficheiros.
No nosso exemplo, o ficheiro de mensagens em francês [messages_fr.properties] conterá o seguinte:
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
O ficheiro [messages_en.properties] terá o seguinte aspeto:
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 ficheiro [messages.properties] é idêntico ao ficheiro [messages_en.properties]. Em última análise, o navegador do cliente poderá escolher entre páginas em francês e páginas em inglês.
Voltemos ao ficheiro [faces-config.xml], que declara o ficheiro 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 no ficheiro de mensagens será referenciada pelo identificador msg nas páginas JSF. Este identificador é utilizado no ficheiro [index.xhtml] discutido:
<?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> na linha 8 irá apresentar o valor da mensagem (presença do identificador msg) com a chave welcome.title. Esta mensagem é pesquisada e encontrada no ficheiro [messages.properties] para o idioma atualmente ativo. Por exemplo, para o francês:
welcome.titre=Tutoriel JSF (JavaServer Faces)
Uma mensagem segue o formato chave=valor. A linha 8 do ficheiro [index.xhtml] fica da seguinte forma após a avaliação da expressão #{msg['welcome.title']}:
<title><h:outputText value="Tutoriel JSF (JavaServer Faces)" /></title>
Este mecanismo de ficheiros de mensagens facilita a alteração do idioma das páginas num projeto JSF. Isto é designado por internacionalização do projeto, ou mais comumente pela sua abreviatura 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 a explorar o conteúdo do ficheiro [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 consiste geralmente em:
- tags de campos de entrada (texto, botões de opção, caixas de seleção, listas suspensas, etc.);
- tags de validação de formulário (botões, links). É através de um botão ou de um link que o utilizador envia a sua entrada para o servidor, que a irá processar,
Qualquer tag JSF pode ser identificada por um atributo id. Na maioria das vezes, isso não é necessário, e é o caso da maioria das tags JSF utilizadas aqui. No entanto, este atributo é útil em determinadas situações. Na linha 17, o formulário é identificado pelo id «form». 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 de duas colunas. Ela gera a tag HTML `<table>`.
- O formulário tem três links que acionam o seu processamento, nas linhas 19, 20 e 23. A tag <h:commandLink> tem pelo menos dois atributos:
- value: o texto do link;
- action: uma cadeia de caracteres C ou a referência a um método que, ao ser executado, devolve a cadeia de caracteres C. Esta cadeia de caracteres C pode ser:
- o nome de uma página JSF no projeto,
- ou um nome definido nas regras de navegação do ficheiro [faces-config.xml] e associado a uma página JSF no projeto;
Em ambos os casos, a página JSF é apresentada assim que a ação definida pelo atributo «action» for executada.
Vamos examinar o funcionamento do processamento de formulários usando o link na linha 13 como exemplo:
<h:commandLink value="#{msg['welcome.langue1']}" action="#{changeLocale.setFrenchLocale}"/>}"/>
Primeiro, o ficheiro de mensagens é utilizado para substituir a expressão #{msg['welcome.langue1']} pelo seu valor. Após a avaliação, a tag passa a ser:
<h:commandLink value="Français" action="#{changeLocale.setFrenchLocale}"/>}"/>
A tradução HTML desta tag JSF será a seguinte:
<a href="#" 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:
![]() |
Repare no atributo onclick da tag HTML <a>. Quando o utilizador clicar no link [Francês], será executado código JavaScript. Este código está incorporado na página recebida pelo navegador, e é o navegador que o executa. O JavaScript é amplamente utilizado no JSF e no AJAX (Asynchronous JavaScript and XML). O seu objetivo geral é melhorar a usabilidade e a capacidade de resposta das aplicações web. Na maioria das vezes, é gerado automaticamente por ferramentas de software, pelo que não é necessário compreendê-lo. No entanto, por vezes, um programador pode precisar de adicionar código JavaScript às suas páginas JSF. Nesses casos, é necessário ter conhecimentos de JavaScript.
Não é necessário aqui compreender o código JavaScript gerado para a tag <h:commandLink> do JSF. No entanto, há dois pontos que merecem destaque:
- o código JavaScript utiliza o ID do formulário que atribuímos à tag <h:form> do JSF,
- o JSF gera identificadores automáticos para todas as tags em que o atributo id não tenha sido definido. Aqui está um exemplo: j_idt8. Atribuir identificadores claros às tags facilita a compreensão do código JavaScript gerado, se necessário. Isto é particularmente verdade quando o programador tem de adicionar ele próprio código JavaScript para manipular os componentes da página. Nesse caso, precisa de saber os IDs dos seus componentes.
O que acontecerá quando o utilizador clicar na ligação [Francês] na página acima? Vamos considerar a arquitetura de uma aplicação JSF:
![]() |
O controlador [Faces Servlet] receberá o pedido do navegador do cliente no seguinte formato HTTP:
- Linhas 1-2: O navegador solicita a URL [http://localhost:8080/mv-jsf2-02/faces/index.xhtml]. Isto acontece sempre: as entradas feitas num formulário JSF obtido inicialmente através da URL URLFormulaire são enviadas para essa mesma URL. O navegador tem duas formas de enviar os valores introduzidos: GET e POST. Com o método GET, os valores introduzidos são enviados pelo navegador na URL solicitada. 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
Ao utilizar o método POST aqui, o navegador envia os valores introduzidos para o servidor através da linha 6.
- linha 3: especifica o formato de codificação dos valores do formulário,
- linha 4: especifica o tamanho em bytes da linha 6,
- linha 5: uma linha vazia indicando o fim dos cabeçalhos HTTP e o início dos 126 bytes de valores do formulário,
- linha 6: os valores do formulário no formato elemento1=valor1&elemento2=valor2& ..., o formato de codificação definido pela linha 3. Neste formato de codificação, certos caracteres são substituídos pelos seus valores hexadecimais. É o caso do último elemento:
formulaire=formulaire&javax.faces.ViewState=...&formulaire%3Aj_idt8=formulaire%3Aj_idt8
onde %3A representa o caractere dois pontos. Assim, a cadeia de caracteres form:j_idt8=form:j_idt8 é enviada para o servidor. Talvez se lembre de que já nos deparámos com o identificador j_idt8 quando analisámos o código HTML gerado para a tag
<h:commandLink value="#{msg['welcome.langue1']}" action="#{changeLocale.setFrenchLocale}"/>
Este tinha sido gerado automaticamente pelo JSF. O que importa aqui é que a presença deste identificador na cadeia de valores enviada pelo navegador do cliente permite ao JSF saber que o link [French] foi clicado. Em seguida, utilizará o atributo action acima para determinar como processar a cadeia recebida. O atributo action="#{changeLocale.setFrenchLocale}" indica ao JSF que o pedido do cliente deve ser tratado pelo método [setFrenchLocale] de um objeto chamado changeLocale. Recorde-se que este bean foi definido por 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. Se este atributo estiver em falta, o nome da classe é utilizado como nome do bean, com o primeiro caractere em minúscula.
Voltemos à solicitação do navegador:
![]() |
e à tag <h:commandLink> que gerou o link [Francês] em que clicámos:
<h:commandLink value="#{msg['welcome.langue1']}" action="#{changeLocale.setFrenchLocale}"/>
O controlador encaminhará o pedido do navegador para o manipulador de eventos definido pelo atributo action da tag <h:commandLink>. O manipulador de eventos M referenciado pelo atributo action de um comando <h:commandLink> deve ter a seguinte assinatura:
- não recebe quaisquer parâmetros. Veremos que, mesmo assim, pode aceder à solicitação do cliente;
- e deve devolver um resultado C do tipo String. Esta string C pode ser:
- ou o nome de uma página JSF no projeto;
- ou um nome definido nas regras de navegação do ficheiro [faces-config.xml] e associado a uma página JSF no projeto;
- ou um ponteiro nulo, se o navegador do cliente não deva mudar de página,
Na arquitetura JSF acima, o controlador [Faces Servlet] utilizará a string C devolvida pelo manipulador de eventos e, se necessário, o seu ficheiro de configuração [faces-config.xml] para determinar qual a página JSF que deve enviar em resposta ao cliente [4].
Na tag
<h:commandLink value="#{msg['welcome.langue1']}" action="#{changeLocale.setFrenchLocale}"/>
o manipulador de eventos para o clique no link [Francês] é o método [changeLocale.setFrenchLocale], onde changeLocale é uma instância da classe [ utils.ChangeLocale] já discutida:
package utils;
import java.io.Serializable;
import javax.enterprise.context.SessionScoped;
import javax.faces.bean.ManagedBean;
@ManagedBean
@SessionScoped
public class ChangeLocale implements Serializable{
// page locale
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 tem, de facto, a assinatura de um manipulador de eventos. Lembre-se de que o manipulador de eventos deve processar o pedido do cliente. Uma vez que não recebe quaisquer parâmetros, como pode aceder ao pedido? Existem várias formas de o fazer:
- O bean B que contém o manipulador de eventos para a página JSF P é frequentemente também aquele que contém o modelo M para essa página. Isto significa que o bean B contém campos que serão inicializados com os valores introduzidos na página P. Isto é feito pelo controlador [Faces Servlet] antes de o manipulador de eventos do bean B ser chamado. Este manipulador terá, portanto, acesso, através dos campos do bean B ao qual pertence, aos valores introduzidos pelo cliente no formulário e poderá processá-los.
- O método estático [FacesContext.getCurrentInstance()] do tipo [FacesContext] permite aceder ao contexto de execução do pedido JSF atual, que é um objeto do tipo [FacesContext]. O contexto de execução do pedido obtido desta forma permite aceder aos parâmetros enviados para o servidor pelo navegador do cliente através do seguinte método:
Se os parâmetros enviados (POST) pelo navegador do cliente forem os seguintes:
o método getRequestParameterMap() irá devolver o seguinte dicionário:
chave | valor |
form | form |
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 manipulador de eventos locale.setFrenchLocale? Pretendemos que este defina o idioma utilizado pela aplicação. Na gíria Java, isto denomina-se «localizar» a aplicação. Esta localização é utilizada pela tag <f:view> na página JSF [index.xhtml]:
<f:view locale="#{changeLocale.locale}">
...
</f:view>
Para mudar a página para francês, basta definir o atributo locale como "fr". Para mudar para inglês, defina-o como "en". O valor do atributo locale é obtido utilizando a expressão [ChangeLocale].getLocale(). Esta expressão devolve o valor do campo locale na classe [ChangeLocale]. A partir daí, derivamos o código para o método [ChangeLocale].setFrenchLocale(), que deve mudar as páginas para francês:
public String setFrenchLocale(){
locale="fr";
return null;
}
Explicámos que um manipulador de eventos deve devolver uma cadeia de caracteres ao estilo C que será utilizada pelo [Faces Servlet] para localizar a página JSF a enviar em resposta ao navegador do cliente. Se a página a devolver for a mesma que está atualmente a ser processada, o manipulador de eventos pode simplesmente devolver o valor null. É isso que se faz aqui na linha 3: queremos devolver a mesma página [index.xhtml], mas num idioma diferente.
Voltemos à arquitetura de processamento de pedidos:
![]() |
O manipulador de eventos changeLocale.setFrenchLocale foi executado e devolveu o valor null ao controlador [Faces Servlet]. O controlador irá, portanto, voltar a apresentar a página [index.xhtml]. Vamos dar mais uma olhadela:
<?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, é utilizado um dos ficheiros de mensagens [messages.properties]. O ficheiro utilizado é aquele que corresponde à «localização» da página (linha 6). Uma vez que o manipulador de eventos changeLocale.setFrenchLocale define esta localização como fr, será utilizado o ficheiro [messages_fr.properties]. Clicar na ligação [English] (linha 14) alterará a localização para en (ver o método changeLocale.setEnglishLocale). O ficheiro [messages_en.properties] será então utilizado e a página aparecerá em inglês:
![]() | ![]() |
Sempre que a página [index.xhtml] é exibida, a tag <f:view> é executada:
<f:view locale="#{changeLocale.locale}">
e, por conseguinte, o método [ChangeLocale].getLocale() é reexecutado. Uma vez que atribuímos ao nosso bean o âmbito Session:
@ManagedBean
@SessionScoped
public class ChangeLocale implements Serializable{
a localização efetuada durante um pedido é mantida para pedidos subsequentes.
Há um último elemento da página [index.xhtml] a ser examinado:
<?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> na linha 17 tem um atributo action definido como uma string. Neste caso, nenhum manipulador de eventos é chamado para processar a página. O utilizador é imediatamente redirecionado para a página [page1.xhtml]. Vamos examinar como a aplicação funciona neste caso de utilização:
![]() |
O utilizador clica na ligação [Página 1]. O formulário é enviado para o controlador [Faces Servlet]. O controlador reconhece na solicitação que recebe que a ligação [Página 1] foi clicada. Ele examina a tag correspondente:
<h:commandLink value="#{msg['welcome.page1']}" action="page1"/>
Não existe nenhum manipulador de eventos associado ao link. O controlador [Faces Servlet] passa imediatamente para o passo [3] acima e apresenta a página [page1.xhtml]:
![]() | ![]() |
2.4.7. A página JSF [page1.xhtml]
A página [page1.xhtml] envia o seguinte fluxo para o navegador do cliente:
![]() |
O código que gera esta 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 ainda não tenha sido explicado. O leitor deve estabelecer a ligação entre o código JSF e a página enviada para o navegador do cliente. O link de volta à página inicial:
<h:commandLink value="#{msg['page1.welcome']}" action="index"/>
exibirá a página [index.xhtml].
2.4.8. Executar o projeto
O nosso projeto está agora concluído. Podemos compilá-lo (Limpar e Compilar):
![]() |
- A compilação do projeto cria a pasta [target] no separador [Ficheiros]. Dentro desta pasta, encontrará o arquivo do projeto [mv-jsf2-02-1.0-SNAPSHOT.war]. Este é o arquivo que é implementado no servidor;
- em [WEB-INF/classes] [2], encontrará as classes compiladas da pasta [Source Packages] do projeto, bem como os ficheiros que estavam no ramo [Other Sources], neste caso os ficheiros de mensagens,
- em [WEB-INF/lib] [3], encontrará as bibliotecas do projeto,
- na raiz de [WEB-INF] [4], encontrará os ficheiros de configuração do projeto,
![]() |
- na raiz do arquivo [5], encontrará as páginas JSF que estavam no ramo [Web Pages] do projeto,
- Assim que o projeto estiver compilado, pode ser executado [6]. Será executado de acordo com a sua configuração de tempo de execução [7],
- o servidor Tomcat será iniciado, caso ainda não estivesse a funcionar [8],
- o arquivo [mv-jsf2-02-1.0-SNAPSHOT.war] será carregado no servidor. Isso é chamado de implantação do projeto no servidor de aplicações,
- em [9], é solicitado que inicie um navegador após a execução. O navegador irá solicitar o contexto da aplicação [10], ou seja, o URL [http://localhost:8080/mv-jsf2-02]. De acordo com as regras no ficheiro [web.xml] (ver página 44), o ficheiro [faces/index.xhtml] será servido ao navegador do cliente. Uma vez que a URL está no formato [/faces/*], será tratada pelo controlador [Faces Servlet] (ver [web.xml] na página 44). Este controlador irá processar a página e enviar a seguinte saída HTML:
![]() |
- O controlador [Faces Servlet] irá então tratar os eventos que ocorrem nesta página.
2.4.9. O ficheiro de configuração [faces-config.xml]
Utilizámos o seguinte ficheiro [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>
Esta é a estrutura mínima de ficheiros para uma aplicação JSF 2 internacionalizada. Aqui, utilizámos novas funcionalidades do JSF 2 em comparação com o JSF 1:
- declarar beans e o seu âmbito utilizando as anotações @ManagedBean, @RequestScoped, @SessionScoped e @ApplicationScoped,
- navegando entre páginas utilizando os nomes das páginas XHTML (sem a extensão .xhtml) como chaves de navegação.
Pode optar por não utilizar estas funcionalidades e, em vez disso, declarar estes elementos do projeto JSF no [faces-config.xml], tal como no JSF 1. Nesse caso, o ficheiro [faces-config.xml] poderá ter o seguinte aspeto:
<?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 -->
<application>
<resource-bundle>
<base-name>
messages
</base-name>
<var>msg</var>
</resource-bundle>
</application>
<!-- managed beans -->
<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>
<!-- navigation -->
<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: âmbito do bean. Os valores possíveis são request, session, application,
- linhas 27–34: declaração de uma regra de navegação:
- linha 28: a regra pode ser descrita. Aqui, não o fizemos;
- linha 29: a página a partir da qual a navegação começa (ponto de partida);
- linhas 30–33: um caso de navegação. Podem existir vários;
- linha 31: a chave de navegação;
- linha 32: a página para a qual está a navegar.
As regras de navegação podem ser apresentadas de forma mais visual. Ao editar o ficheiro [faces-config.xml], pode utilizar o separador [PageFlow]:
![]() |
Suponhamos que utilizemos o ficheiro [faces-config.xml] anterior. Como é que a nossa aplicação mudaria?
- Na classe [ChangeLocale], as anotações @ManagedBean e @SessionScoped desapareceriam, uma vez que o bean está agora declarado no [faces-config],
- A navegação de [index.xhtml] para [page1.xhtml] através de um link passaria a ser:
<h:commandLink value="#{msg['welcome.page1']}" action="p1"/>
Ao atributo action é atribuída a chave de navegação p1 definida em [faces-config],
- A navegação de [page1.xhtml] para [index.xhtml] através de um link ficaria assim:
<h:commandLink value="#{msg['page1.welcome']}" action="welcome"/>
Atribuímos a chave de navegação </span>**<span style="color: #000000">welcome</span>**<span style="color: #000000">, definida em [faces-config], ao atributo action;
- os métodos setFrenchLocale e setEnglishLocale, que devem devolver uma chave de navegação, não precisam de ser modificados porque anteriormente devolviam null para indicar que o utilizador permanecia na mesma página.
2.4.10. Conclusão
Voltemos ao projeto NetBeans que escrevemos:
![]() |
Este projeto segue a seguinte arquitetura:
![]() |
Em todos os projetos JSF, encontramos os seguintes elementos:
- Páginas JSF [A] que são enviadas [4] para os navegadores dos clientes pelo controlador [Faces Servlet] [3],
- ficheiros de mensagens [C] que permitem alterar o idioma das páginas JSF,
- classes Java [B] que tratam de eventos que ocorrem no navegador do cliente [2a, 2b] e/ou servem como modelos para as páginas JSF [3]. Na maioria das vezes, as camadas [business] e [DAO] são desenvolvidas e testadas separadamente. A camada [web] é então testada com uma camada [business] fictícia. Se as camadas [business] e [DAO] estiverem disponíveis, trabalhamos normalmente com os seus ficheiros .jar.
- ficheiros de configuração [D] para ligar estes vários elementos entre si. O ficheiro [web.xml] foi descrito na página 44 e raramente será modificado. O mesmo se aplica ao [faces-config], onde utilizaremos sempre a versão simplificada.
2.5. Exemplo mv-jsf2-03: formulário de entrada - Componentes JSF
A partir de agora, deixaremos de mostrar a construção do projeto. Apresentamos projetos já prontos e explicamos como funcionam. O leitor pode descarregar todos os exemplos a partir do site deste documento (ver secção 1.2).
2.5.1. A aplicação
A aplicação tem uma única vista:
![]() |
A aplicação apresenta os principais componentes JSF que podem ser utilizados num formulário de entrada:
- A coluna [1] indica o nome da tag JSF/HTML utilizada,
- a coluna [2] mostra um exemplo de introdução de dados para cada uma das tags encontradas,
- a coluna [3] exibe os valores do bean que serve como modelo da página,
- as entradas feitas em [2] são validadas pelo botão [4]. Esta validação atualiza simplesmente o bean modelo da página. A mesma página é então apresentada novamente. Assim, após a validação, a coluna [3] exibe os novos valores do bean modelo, permitindo ao utilizador verificar o impacto das suas entradas no modelo da página.
2.5.2. O Projeto NetBeans
O projeto NetBeans para a aplicação é o seguinte:
![]() |
- em [1], os ficheiros 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 ficheiro de mensagens da aplicação em duas línguas: francês e inglês.
2.5.3. O ficheiro [pom.xml]
Estamos a mostrar 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>
Estas são as dependências necessárias para um projeto JSF. Nos exemplos que se seguem, este ficheiro só será apresentado quando houver alterações.
2.5.4. O ficheiro [ web.xml]
O ficheiro [web.xml] foi configurado de forma a 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 [Faces Servlet]. Solicita que os comentários num facelet, tais como:
<!-- langues -->
sejam ignorados. Sem este parâmetro, os comentários causam problemas difíceis de compreender,
- linhas 3–6: um parâmetro para o [Faces Servlet] que será explicado um pouco mais adiante.
2.5.5. O ficheiro [faces-config.xml]
O ficheiro [faces-config.xml] da aplicação é 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: configure o ficheiro de mensagens da aplicação.
2.5.6. O ficheiro de mensagens [messages.properties]
Os ficheiros de mensagens (ver [5] na captura de ecrã 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
Estas mensagens são apresentadas 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] para a 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 este conceito de modelo com um exemplo retirado da página [index.xhtml]:
<!-- line 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}"/>
Quando a página [index.xhtml] é solicitada pela primeira vez, o código acima gera a linha 2 da tabela de entrada:
![]() |
A linha 2 exibe o campo [1], as linhas 3–6 exibem o campo [2] e a linha 7 exibe o campo [3].
As linhas 5 e 7 utilizam uma expressão EL que faz referência ao bean de formulário definido na classe [Form.java] da seguinte forma:
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. Este será, portanto, o nome da classe, começando por uma letra minúscula: form,
- O bean tem âmbito de solicitação. Isto significa que, num ciclo de solicitação do cliente/resposta do servidor, ele é instanciado quando a solicitação o necessita e destruído quando a resposta ao cliente é devolvida.
No código abaixo, da página [index.xhtml]:
<!-- line 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 do formulário. Para compreender as ligações entre uma página P e o seu modelo M, devemos voltar ao ciclo de pedido do cliente/resposta do servidor ( ) que caracteriza uma aplicação web:
![]() |
Devemos distinguir entre o caso em que a página P é enviada como resposta ao navegador (passo 4) — por exemplo, durante a solicitação inicial da página — e o caso em que o utilizador aciona um evento na página P, que é então tratado pelo controlador [Faces Servlet] (passo 1).
Podemos distinguir estes dois casos observando-os da perspetiva do navegador:
- durante o pedido inicial da página, o navegador executa uma operação GET na URL da página,
- ao enviar valores introduzidos na página, o navegador executa uma operação POST na URL da página.
Em ambos os casos, é solicitada a mesma URL. Dependendo de a solicitação do navegador ser GET ou POST, a solicitação será processada de forma diferente.
[Caso 1 – Pedido inicial da página P]
O navegador solicita o URL da página utilizando uma solicitação GET. O controlador [Faces Servlet] avançará diretamente para o passo [4] de renderização da resposta, e a página [index.xhtml] será enviada ao cliente. O controlador JSF instruirá cada tag na página a ser renderizada. Tomemos o exemplo da linha 5 do código [index.xhtml]:
<h:inputText id="inputText" value="#{form.inputText}"/>
A tag JSF <h:inputText value="value"/> gera a tag HTML <input type="text" value="value"/>. A classe responsável pelo processamento desta tag encontra a expressão #{form.inputText}, que deve avaliar:
- se o bean do formulário ainda não existir, este é criado através da instanciação da classe forms.Form,
- a expressão #{form.inputText} é avaliada chamando o método form.getInputText(),
- O texto <input id="form:inputText" type="text" name="form:inputText" value="text" /> é inserido no fluxo HTML que será enviado ao cliente, partindo do princípio de que o método form.getInputText() devolveu a cadeia de caracteres "text". O JSF também atribuirá um nome (name) ao componente HTML colocado no fluxo. Este nome é construído a partir dos identificadores id do componente JSF analisado e dos seus componentes pai, neste caso a tag <h:form id="form"/>.
Note que, se numa página P utilizar a expressão #{M.field}, em que M é o bean de modelo da página P, este bean deve ter um método getField() público. O tipo devolvido por este método deve ser convertível para um tipo String. Um modelo M comum é o seguinte:
onde T é um tipo que pode ser convertido para um String, possivelmente utilizando um método toString.
Ainda no que diz respeito à exibição da página P, o processamento da linha:
<h:outputText value="#{form.inputText}"/>
será semelhante, e será gerada a seguinte saída HTML:
Internamente no servidor, a página P é representada como uma árvore de componentes, espelhando a árvore de tags da página enviada ao cliente. Iremos referir-nos a esta árvore como a visualização da página ou . Este estado é armazenado. Pode ser armazenado de duas formas, dependendo de uma configuração no ficheiro [web.xml] da aplicação:
<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–11 definem o controlador [Faces Servlet]. Este pode ser configurado utilizando várias tags <context-param>, incluindo a das linhas 3–6, que especifica que o estado de uma página deve ser guardado no cliente (o navegador). O outro valor possível, na linha 5, é **server** para indicar o armazenamento no servidor. Este é o valor predefinido.
Quando o estado de uma página é guardado no cliente, o controlador JSF adiciona um campo oculto a todas as páginas HTML que envia, cujo valor é o estado atual da página. Este campo oculto tem o seguinte formato:
<input type="hidden" name="javax.faces.ViewState" id="javax.faces.ViewState" value="H4sIAAAAAAAAANV...Bnoz8dqAAA=" />
O seu valor representa, de forma codificada, o estado da página enviada ao cliente. É importante compreender que este campo oculto faz parte do formulário da página e, por isso, será incluído nos valores enviados pelo navegador quando o formulário for submetido. Utilizando este campo oculto, o controlador JSF consegue restaurar a vista tal como foi enviada ao cliente.
Quando o estado de uma página é guardado no servidor, o estado da página enviada ao cliente é guardado na sessão do cliente. Quando o navegador do cliente submete os valores introduzidos no formulário, também envia o seu token de sessão. Utilizando este token, o controlador JSF irá recuperar o estado da página enviada ao cliente e restaurá-lo.
A codificação do estado de uma página JSF pode exigir várias centenas de bytes. Uma vez que este estado é mantido para cada utilizador da aplicação, podem surgir problemas de memória se houver um grande número de utilizadores. Por esta razão, optámos aqui por guardar o estado da página no cliente (ver [web.xml], secção 2.5.4, página 66).
[Caso 2 – Processamento da página P]
![]() |
Estamos na etapa [1] acima, onde o controlador [Faces Servlet] receberá um pedido POST do navegador do cliente para o qual enviou anteriormente a página [index.xhtml]. Estamos a lidar com o processamento de um evento de página. Várias etapas ocorrerão antes que o evento possa sequer ser processado em [2a]. O ciclo de processamento de um pedido POST pelo controlador JSF é o seguinte:

- Em [A], graças ao campo oculto `javax.faces.ViewState`, a vista inicialmente enviada para o navegador do cliente é reconstruída. Aqui, os componentes da página recuperam os valores que tinham na página enviada. O nosso componente `inputText` recupera o seu valor "text",
- em [B], os valores enviados pelo navegador do cliente são utilizados para atualizar os componentes da vista. Assim, se no campo de entrada HTML denominado inputText, o utilizador tiver digitado «jean», o valor «jean» substitui o valor «text». A vista reflete agora a página tal como foi modificada pelo utilizador e já não tal como foi enviada ao navegador,
- em [C], os valores enviados são verificados. Suponha que o componente inputText anterior seja um campo de entrada de idade. O valor introduzido deve ser um número inteiro. Os valores enviados pelo navegador são sempre do tipo String. O seu tipo final no modelo M associado à página P pode ser totalmente diferente. Há então uma conversão de um tipo String para outro tipo T. Esta conversão pode falhar. Neste caso, o ciclo de pedido/resposta é encerrado e a página P construída em [B] é reenviada para o navegador do cliente com mensagens de erro, caso o autor da página P as tenha fornecido. Note-se que o utilizador vê a página exatamente como a introduziu, sem qualquer esforço por parte do programador. Noutra tecnologia, como o JSP, o programador deve reconstruir a página P ele próprio utilizando os valores introduzidos pelo utilizador. O valor de um componente também pode ser submetido a um processo de validação. Continuando com o exemplo do componente inputText, que é o campo de entrada da idade, o valor introduzido não deve ser apenas um número inteiro, mas um número inteiro dentro do intervalo [1,N]. Se o valor introduzido passar na etapa de conversão, pode falhar na etapa de validação. Neste caso, o ciclo de pedido/resposta também é concluído e a página P construída em [B] é enviada de volta para o navegador do cliente,
- em [D], se todos os componentes da página P passarem pelas etapas de conversão e validação, os 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 este valor será atribuído ao modelo de formulário da página através da execução do código form.setInputText("jean"). Note-se que, no modelo M da página P, os campos privados de M que armazenam o valor de um campo de entrada em P devem ter um método set,
- assim que o modelo M da página P for atualizado com os valores enviados, o evento que desencadeou o POST da página P pode ser processado. Esta é a etapa [E]. Note que, se o manipulador deste evento pertencer ao bean M, ele tem acesso aos valores do formulário P que foram armazenados nos campos desse mesmo bean.
- A etapa [E] devolve uma chave de navegação ao controlador JSF. Nos nossos exemplos, esta será sempre o nome da página XHTML a ser apresentada, sem o sufixo .xhtml. Esta é a etapa [F]. Outra abordagem consiste em devolver uma chave de navegação que será consultada no ficheiro [faces-config.xml]. Já descrevemos este caso.
A partir do exposto, podemos concluir que:
- uma página P exibe os campos C do seu modelo M utilizando os métodos [M].getC(),
- os campos C do modelo M numa página P são inicializados com os valores introduzidos na página P utilizando os métodos [M].setC(input). Nesta etapa, podem ocorrer processos de conversão e validação que podem falhar. Neste caso, o evento que desencadeou o POST da página P não é processado, e a página é reenviada para o cliente exatamente como o cliente a introduziu.
O modelo [Form.java] para a página [index.xhtml] será o seguinte:
package forms;
import javax.enterprise.context.RequestScoped;
import javax.faces.bean.ManagedBean;
@ManagedBean
@RequestScoped
public class Form {
/** Creates a new instance of Form */
public Form() {
}
// form fields
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";
// events
public String submit(){
return null;
}
// getters and setters
...
}
Os campos nas linhas 16–27 são utilizados nas seguintes partes 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">
<!-- languages -->
<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">
<!-- headers -->
<h:outputText value="#{msg['form.headerCol1']}" styleClass="entete"/>
<h:outputText value="#{msg['form.headerCol2']}" styleClass="entete"/>
<h:outputText value="#{msg['form.headerCol3']}" styleClass="entete"/>
<!-- line 1 -->
...
<!-- line 2 -->
...
<!-- line 3 -->
...
<!-- line 4 -->
...
<!-- line 5 -->
...
<!-- line 6 -->
...
<!-- line 7 -->
...
<!-- line 8 -->
...
<!-- line 9 -->
...
<!-- line 10 -->
...
<!-- line 11 -->
...
<!-- line 12 -->
...
</h:panelGrid>
<p>
<h:commandButton type="submit" id="submit" value="#{msg['form.submitText']}"/>
</p>
</h:form>
</h:body>
</f:view>
</html>
Vamos examinar os principais componentes desta página, um por um. Observe 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 do formulário devem estar dentro de uma tag <h:form> (linhas 12–16). A tag <f:view> (linhas 7–18) é necessária se a aplicação for internacionalizada. Além disso, um formulário deve ter uma forma de ser enviado (POST), frequentemente um link ou um botão, como na linha 14. Também pode ser enviado por vários eventos (alterar uma seleção numa lista, alterar o campo ativo, digitar um caractere num campo de entrada, etc.).
2.5.9. O estilo do formulário
Para tornar as colunas da tabela do formulário mais legíveis, o formulário inclui uma folha de estilos:
<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 head do HTML utilizando a seguinte tag:
<h:outputStylesheet library="css" name="styles.css"/>
A folha de estilo estará localizada na pasta [resources]:
![]() |
Na tag:
<h:outputStylesheet library="css" name="styles.css"/>
- library é o nome da pasta que contém a folha de estilos,
- name é o nome da folha de estilo.
Vejamos um exemplo de como utilizar esta folha de estilo:
<h:panelGrid columnClasses="col1,col2,col3" columns="3" border="1">
A tag <h:panelGrid columns="3"/> define uma grelha de três colunas. O atributo columnClasses é utilizado para aplicar estilos a estas colunas. Os valores col1, col2 e col3 no atributo columnClasses especificam os respetivos estilos para as colunas 1, 2 e 3 da grelha. Estes estilos são consultados 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,
Estes três estilos definem a cor de fundo de cada coluna.
- linhas 19–23: o estilo `entete` é utilizado para definir o estilo do texto na primeira linha da tabela:
<!-- entêtes -->
<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» é utilizado para definir o estilo do texto na primeira coluna da tabela:
<!-- ligne 1 -->
<h:outputText value="inputText" styleClass="info"/>
Não nos deteremos na utilização de folhas de estilo, uma vez que estas, por si só, merecem um livro e, além disso, o seu desenvolvimento é frequentemente confiado a especialistas. No entanto, optámos por utilizar uma folha de estilo minimalista para lembrar aos leitores que a sua utilização é essencial.
Agora, vamos ver como a imagem de fundo da página foi definida:
<h:body style="background-image: url('${request.contextPath}/resources/images/standard.jpg');">
A imagem de fundo é definida pelo atributo style da tag <h:body>. Este atributo permite definir elementos de estilo. A imagem de fundo está localizada na pasta [resources/images/standard.jpg]:
![]() |
Esta imagem é acedida através do URL [/mv-jsf2-03/resources/images/standard.jpg]. Por conseguinte, poderíamos escrever:
<h:body style="background-image: url('mv-jsf2-03/resources/images/standard.jpg');">
/mv-jsf2-03 é o contexto da aplicação. Este contexto é definido pelo administrador do servidor web e, por isso, pode sofrer alterações. Este contexto pode ser obtido utilizando a expressão EL ${request.contextPath}. Por conseguinte, preferimos o seguinte atributo de estilo:
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
Vamos rever o que já foi explicado na Secção 2.5.7 num caso geral e aplicá-lo ao formulário em questão. Este formulário será testado no ambiente JSF padrão:
![]() |
Aqui, não haverá manipuladores de eventos nem camada [de negócios]. As etapas [2x] não existirão, portanto. Distinguiremos entre o caso em que o formulário F é inicialmente solicitado pelo navegador e o caso em que o utilizador aciona um evento no formulário F, que é então processado pelo controlador [Faces Servlet]. Existem dois ciclos distintos de solicitação do cliente/resposta do servidor.
- O primeiro, correspondente à solicitação inicial da página, é acionado por uma operação GET do navegador na URL do formulário,
- o segundo, correspondente ao envio dos valores introduzidos na página, é acionado por uma operação POST nesse mesmo URL.
Dependendo de a solicitação do navegador ser GET ou POST, o controlador [Faces Servlet] processa a solicitação de forma diferente.
[Caso 1 – Pedido inicial para o formulário F]
O navegador solicita a URL da página com um GET. O controlador [Faces Servlet] avançará diretamente para o passo [4] de renderização da resposta. O formulário [index.xhtml] será inicializado pelo seu modelo [Form.java] e enviado ao cliente, que receberá a seguinte visualização:

As trocas HTTP entre cliente e servidor são as seguintes neste caso:
Pedido HTTP do cliente:
A linha 1 mostra o pedido GET do navegador.
Resposta HTTP do servidor:
Não mostrado aqui, a linha 7 é seguida por uma linha em branco e pelo código HTML do formulário. Este é o código que o navegador interpreta e apresenta.
[Caso 2 – Processamento dos valores introduzidos no Formulário F]
O utilizador preenche o formulário e submete-o utilizando o botão [Submeter]. O navegador envia então um pedido POST para o URL do formulário. O controlador [Faces Servlet] processa este pedido, atualiza o modelo [Form.java] para o formulário [index.xhtml] e devolve o formulário [index.xhtml] atualizado com base neste novo modelo. Vamos examinar este ciclo utilizando um exemplo:

Acima, o utilizador introduziu os seus dados e enviou-os. Em resposta, recebe a seguinte visualização:

As trocas HTTP entre cliente e servidor são as seguintes neste caso:
Pedido HTTP do cliente:
Na linha 1, o pedido POST efetuado pelo navegador. Na linha 14, os valores introduzidos pelo utilizador. Por exemplo, pode ver o texto introduzido no campo de entrada:
Na linha 14, o campo oculto javax.faces.ViewState foi enviado. Este campo representa, de forma codificada, o estado do formulário tal como foi inicialmente enviado ao navegador durante a sua solicitação GET inicial.
Resposta HTTP do servidor:
Não mostrado aqui, a linha 6 é seguida por uma linha em branco e pelo código HTML do formulário, atualizado pelo seu novo modelo derivado da solicitação POST.
Vamos agora examinar os vários componentes deste formulário.
2.5.11. Tag <h:inputText>
A tag <h:inputText> gera uma tag HTML <input type="text" ...>.
Considere o seguinte código:
<!-- line 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 o 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 resultante é a seguinte:
- A linha 2 do código XHTML gera [1],
- a tag <h:panelGroup> (linhas 3–6) permite que vários elementos sejam agrupados numa única célula da tabela gerada pela tag <h:panelGrid> na linha 20 do código completo da página (ver secção 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–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].
A saída HTML gerada pela página XHTML é a 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, abaixo, vamos introduzir um valor no campo de entrada [1] e enviar o formulário utilizando o botão [Submit] [2]. Recebemos a seguinte página em resposta [3, 4]:
![]() |
O valor do campo [1] é enviado da seguinte forma:
Em [2], o formulário é enviado utilizando o seguinte botão:
<h:commandButton id="submit" type="submit" value="#{msg['form.submitText']}"/>
A tag <h:commandButton> não possui o atributo action. Neste caso, nenhum manipulador de eventos é invocado e nenhuma regra de navegação é aplicada. Após o processamento, a mesma página é apresentada. Vamos rever o seu ciclo de processamento:

- em [A], a página P é restaurada exatamente como foi enviada. Isto significa que o componente com id inputText é restaurado com o seu valor inicial "text",
- em [B], os valores enviados pelo navegador (introduzidos pelo utilizador) são atribuídos aos componentes da página P. Aqui, o componente com o id inputText recebe o valor «new text»,
- em [C], ocorrem 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";
Uma vez que os valores introduzidos são do tipo String, não é necessária qualquer conversão. Além disso, não foram criadas regras de validação. Iremos criá-las mais tarde.
- Em [D], os valores introduzidos são atribuídos ao modelo. O campo inputText em [Form.java] recebe o valor "new text",
- em [E], nada acontece porque nenhum manipulador de eventos foi associado ao botão [Validate].
- Em [F], a página P é reenviada ao cliente porque o botão [Validate] não tem um atributo action. São então executadas as seguintes linhas de [index.xhtml]:
<!-- line 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 é «novo texto». Isto resulta na seguinte apresentação:
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 à tag <h:inputText> do JSF, com a diferença de que cada caractere digitado pelo utilizador é visualmente substituído por um asterisco (*).
Considere o seguinte código:
<!-- line 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 o seu modelo em [Form.java]:
private String inputSecret="secret";
Quando a página [index.xhtml] é solicitada pela primeira vez, a página resultante é 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 em [Form.java] deveria ter sido utilizado para gerar o texto para o campo de entrada. Existe uma exceção quando o campo de entrada é do tipo «password». A tag <h:inputSecret> é utilizada apenas para ler uma entrada, não para a exibir.
- 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).
A saída HTML gerada pela página XHTML é a 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 introduzir um valor no campo de entrada [1] e enviar o formulário utilizando o botão [Submit] [2]. Obtemos a seguinte página como resposta [3]:
![]() |
O valor do campo [1] é enviado da seguinte forma:
O envio do formulário através de [2] fez com que o modelo [Form.java] fosse atualizado com a entrada de [1]. O campo inputSecret em [Form.java] recebeu então o valor "mdp". Como o formulário [index.xhtml] não definiu quaisquer regras de navegação ou manipuladores de eventos, é novamente apresentado após a atualização do seu modelo. Somos então redirecionados para a vista exibida quando a página [index.xhtml] foi inicialmente solicitada, onde apenas o valor do campo inputSecret do modelo foi alterado [3].
2.5.13. Tag <h:inputTextArea>
A tag <h:inputTextArea> gera uma tag HTML <textarea ...>text</textarea>. Trata-se de um campo de entrada semelhante ao da tag JSF <h:inputText>, exceto que, neste caso, é possível digitar várias linhas de texto.
Considere o seguinte código:
<!-- line 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 o seu modelo em [Form.java]:
private String inputTextArea="ligne1\nligne2\n";
Quando a página [index.xhtml] é solicitada pela primeira vez, a página resultante é 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]. O seu conteúdo foi gerado através da chamada ao método getInputTextArea do modelo, que devolveu 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 cadeia de caracteres "line1\nline2" continha quebras de linha \n. Estas continuam presentes. No entanto, quando inseridas num fluxo HTML, são apresentadas como espaços pelos navegadores. A tag HTML <textarea>, que apresenta [3], interpreta corretamente as quebras de linha.
A saída HTML gerada pela página XHTML é a 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, a seguir, vamos introduzir um valor no campo de entrada [1] e enviar o formulário utilizando o botão [Enviar] [2]. Recebemos a seguinte página como resposta [3]:
![]() |
O valor do campo [1] que foi enviado é o seguinte:
O envio do formulário através de [2] fez com que o modelo [Form.java] fosse atualizado com a entrada de [1]. O campo textArea em [Form.java] recebeu então o valor "JSF Tutorial\npart1". Ao recarregar [index.xhtml], verifica-se que o campo textArea do modelo foi efetivamente atualizado [3].
2.5.14. Tag <h:selectOneListBox>
A tag <h:selectOneListBox> gera uma tag HTML <select>...</select>. Visualmente, gera uma lista suspensa ou uma lista com uma barra de rolagem.
Considere o seguinte código:
<!-- line 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 o seu modelo em [Form.java]:
private String selectOneListBox1="2";
Quando a página [index.xhtml] é solicitada pela primeira vez, a página resultante é 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 apresente apenas um item. Se este atributo estiver em falta, o valor predefinido do atributo size é 1. Os itens da lista foram gerados pelas tags <f:selectItem> nas linhas 6–8. Estas 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 item. Este é o valor que será enviado para o controlador [Faces Servlet] se o item for selecionado na lista suspensa.
O item exibido em [3] foi determinado através da chamada ao método getSelectOneListBox1() (linha 5). O resultado «2» obtido (linha 1 do código Java) fez com que o item da linha 7 da lista suspensa fosse exibido, uma vez que o seu atributo itemValue é «2»,
- a linha 11 do código XHTML gera [4]. Aqui, o método getSelectOneListBox1 de [Form.java] foi utilizado novamente.
A saída HTML gerada pela página XHTML é a 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–6: as tags HTML <option ...> ... </option> geradas pelas tags JSF <f:selectItem>,
- linha 5: o facto de o elemento com value="2" estar selecionado na lista é refletido pela presença do atributo selected="selected".
Agora, abaixo, vamos selecionar [1] um novo valor da lista e enviar o formulário utilizando o botão [Submit] [2]. Recebemos a seguinte página em resposta [3]:
![]() |
O valor do campo [1] enviado é o seguinte:
O envio do formulário através de [2] fez com que o modelo [Form.java] fosse atualizado com a entrada [1]. O elemento HTML
<option value="3">trois</option>
foi selecionado. O navegador enviou a cadeia de caracteres "3" como o valor do componente JSF que gerou 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. Além disso, após esta atualização, o campo do modelo [Form.java]
private String selectOneListBox1;
contém agora o valor "3".
Quando a página [index.xhtml] é exibida novamente após o processamento, este valor faz com que seja apresentada a visualização [3,4] mostrada acima:
- determina qual o item da lista suspensa que deve ser apresentado [3],
- e o valor do campo selectOneListBox1 é exibido em [4].
Vamos considerar uma variante da tag <h:selectOneListBox>:
<!-- line 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] para a tag <h:selectOneListBox> na linha 5 é o seguinte:
private String selectOneListBox2="3";
Quando a página [index.xhtml] é solicitada pela primeira vez, a página resultante é a seguinte:
![]() |
- A linha 2 do código XHTML gera [1],
- o texto [2] é gerado pela linha 4. A lista com uma barra de deslocamento [3] é gerada pelas linhas [5-11]. É o valor do atributo size="3" que resulta numa lista com uma barra de deslocamento em vez de uma lista suspensa. Os itens da lista foram gerados pelas tags <f:selectItem> nas linhas 6–8,
O elemento selecionado em [3] foi determinado através da 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 apresentado, uma vez que o seu atributo itemValue é «3»,
- a linha 13 do código XHTML gera [4]. Aqui, o método getSelectOneListBox2 de [Form.java] foi utilizado novamente.
A saída HTML gerada pela página XHTML é a 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 facto de o elemento com value="3" estar selecionado na lista resulta na presença do atributo selected="selected".
Agora, abaixo, vamos selecionar [1] um novo valor da lista e enviar o formulário utilizando o botão [Submit] [2]. Obtemos a seguinte página como resposta [3]:
![]() |
O valor indicado para o campo [1] é o seguinte:
O envio do formulário através de [2] fez com que o modelo [Form.java] fosse atualizado com os dados introduzidos em [1]. O elemento HTML
<option value="5">cinq</option>
foi selecionado. O navegador enviou a cadeia de caracteres "5" como o valor do componente JSF 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. Além disso, após esta atualização, o campo
private String selectOneListBox2;
contém agora o valor "5".
Quando a página [index.xhtml] é novamente apresentada após o processamento, este valor dá origem à apresentação mostrada em [3,4] acima:
- determina qual o item da lista que deve ser selecionado [3],
- e o valor do campo selectOneListBox2 é apresentado em [4].
2.5.15. tag <h:selectManyListBox>
A tag <h:selectManyListBox> gera uma tag HTML <select multiple="multiple">...</select> que permite ao utilizador selecionar vários itens de uma lista.
Considere o seguinte código:
<!-- line 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 o 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 resultante é 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 três destes elementos em qualquer momento. Os elementos selecionados na lista foram determinados através da chamada ao método getSelectManyListBox() (linha 5) do modelo Java. O resultado {"1","3"} (linha 1 do código Java) é uma matriz de elementos String. Cada um destes elementos é utilizado para selecionar um dos itens da lista. Aqui, os itens nas linhas 6 e 10 cujo atributo itemValue se encontra na matriz {"1","3"} serão selecionados. Isto é mostrado em [3].
- A linha 14 do código XHTML gera [4]. Aqui, a chamada não é feita para o método getSelectManyListBox do modelo de lista Java, mas sim para o seguinte método getSelectManyListBoxValue:
private String[] selectManyListBox=new String[]{"1","3"};
...
// getters et 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 uma matriz de Strings. Para incluir este elemento na saída HTML, o controlador teria chamado o seu método toString. No entanto, para uma matriz, este método apenas devolve o «hashcode» da matriz e não a lista dos seus elementos, como gostaríamos. Por isso, usamos o método getSelectManyListBoxValue acima para obter uma string que represente o conteúdo da matriz;
- a linha 12 do código XHTML gera o botão [5]. Quando este botão é clicado, o código JavaScript no atributo onclick é executado. Ele será incorporado na página HTML que será gerada pelo código JSF. Para compreender isto, precisamos de conhecer a natureza exata dessa página.
A saída HTML gerada pela página XHTML é a 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 indica que esta é uma lista de seleção múltipla,
- o facto de o modelo da lista ser a matriz de cadeias de caracteres {"1","3"} significa que os itens da lista nas linhas 4 (value="1") e 6 (value="3") têm o atributo selected="selected",
- linha 10: quando o botão [Clear] é clicado, o código JavaScript no atributo onclick é executado. A página é representada no navegador por uma árvore de objetos frequentemente chamada de DOM (Document Object Model). Cada objeto na árvore é acessível ao código JavaScript através do seu atributo name. A lista na linha 3 do código HTML acima é chamada de formulaire:selectManyListBox. O próprio formulário pode ser referido de várias maneiras. Aqui, é referido utilizando a notação this.form, em que this se refere ao botão [Reset] e this.form se refere ao formulário no qual esse botão se encontra. A lista form:selectManyListBox está localizada dentro deste mesmo formulário. Assim, a notação this.form['form:selectManyListBox'] refere-se à localização da lista na árvore de componentes do formulário. O objeto que representa uma lista possui um atributo selectedIndex cujo valor é o índice do item selecionado na lista. Este índice começa em 0 para indicar o primeiro item da lista. O valor -1 indica que nenhum item está selecionado na lista. O código JavaScript que define o atributo selectedIndex como -1 desmarca todos os itens da lista, caso algum estivesse selecionado.
Agora, abaixo, vamos selecionar [1] novos valores da lista (para selecionar vários itens na lista, mantenha premida a tecla Ctrl enquanto clica) e enviar o formulário utilizando o botão [Submit] [2]. Recebemos a seguinte página em resposta [3,4]:
![]() |
O valor do campo [1] enviado é o seguinte:
O envio do formulário através de [2] fez com que o modelo [Form.java] fosse atualizado com a entrada [1]. Os elementos HTML
<option value="3">trois</option>
<option value="4">quatre</option>
<option value="5">cinq</option>
foram selecionadas. O navegador enviou as três cadeias de caracteres "3", "4", "5" como valores para o 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 este 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 é uma matriz de Strings. Aqui, será a matriz {"3", "4", "5"}. Após esta atualização, o campo
private String[] selectManyListBox;
contém agora a matriz {"3","4","5"}.
Quando a página [index.xhtml] é exibida novamente após o processamento, este valor faz com que seja exibido [3,4] acima:
- determina quais os itens da lista que devem ser selecionados [3],
- e 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:
<!-- line 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 para a tag <h:selectOneMenu> em [Form.java] é o seguinte:
private String selectOneMenu="1";
Quando a página [index.xhtml] é solicitada pela primeira vez, o código acima gera a visualização:
![]() |
Um exemplo de execução pode ser o seguinte:
![]() |
O valor indicado 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:
<!-- line 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 para a tag <h:selectManyMenu> em [Form.java] é o seguinte:
private String[] selectManyMenu=new String[]{"1","2"};
Quando a página [index.xhtml] é solicitada pela primeira vez, o código acima gera a página:
![]() |
A Lista [1] contém os textos «um», ..., «cinco», com os itens «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>
Conforme mostrado acima nas linhas 4 e 5, os elementos «one» e «two» estão selecionados (presença do atributo selected).
É difícil fornecer uma captura de ecrã de um exemplo disto em ação, porque não podemos mostrar os itens selecionados no menu. Recomendamos aos leitores que experimentem por si próprios (para selecionar vários itens na lista, mantenha premida a tecla Ctrl enquanto clica).
2.5.18. Tag <h:inputHidden>
A tag <h:inputHidden> não tem representação visual. É utilizada exclusivamente para inserir uma tag HTML <input type="hidden" value="..."/> no fluxo HTML da página. Quando incluídos dentro de uma tag <h:form>, os seus valores fazem parte dos dados enviados para o servidor quando o formulário é submetido. Como se trata de campos de formulário que o utilizador não consegue ver, são chamados de campos ocultos. O objetivo destes campos é preservar os dados entre os diferentes ciclos de pedido/resposta do mesmo cliente:
- o cliente solicita um formulário F. O servidor envia-o e coloca a informação I num campo oculto C, na forma <h:inputHidden id="C" value="I"/>,
- quando o cliente preenche o formulário F e o envia para o servidor, o valor I do campo C é reenviado para o servidor. O servidor pode então recuperar a informação I que tinha armazenado na página. Isto cria uma memória entre os dois ciclos de pedido/resposta,
- o próprio JSF utiliza esta técnica. A informação I que armazena no formulário F é o valor de todos os seus componentes. Utiliza o seguinte campo oculto para isso:
<input type="hidden" name="javax.faces.ViewState" id="javax.faces.ViewState" value="H4sIAAAAAAAAANV...8PswawAA" />
O campo oculto chama-se javax.faces.ViewState e o seu valor é uma cadeia de caracteres que representa, de forma codificada, os valores de todos os componentes da página enviada ao cliente. Quando o cliente submete a página após introduzir dados no formulário, o campo oculto javax.faces.ViewState é enviado de volta juntamente com os valores introduzidos. Isto permite ao controlador JSF reconstruir a página tal como foi originalmente enviada. Este mecanismo foi explicado na página 72.
O código JSF para o exemplo é o seguinte:
<!-- ligne 9 -->
<h:outputText value="inputHidden" styleClass="info"/>
<h:inputHidden id="inputHidden" value="#{form.inputHidden}"/>
<h:outputText value="#{form.inputHidden}"/>
O modelo para a tag <h:inputHidden> em [Form.java] é o seguinte:
private String inputHidden="initial";
Isto resulta na seguinte apresentação quando a página [index.xhtml] é solicitada pela primeira vez:
- 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>
Quando o formulário for enviado, o valor "initial" do campo denominado form:inputHidden na linha 3 será enviado juntamente com os outros valores do formulário. O campo
private String inputHidden;
será atualizado com este valor, que é o que já tinha inicialmente. Este valor será incluído na nova página enviada de volta ao cliente. Por isso, obtemos sempre a captura de ecrã 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" ...>.
Considere o seguinte código JSF:
<!-- line 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 para a tag <h:selectBooleanCheckbox> na 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 resultante é 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, foi utilizado o método getSelectBooleanCheckbox de [Form.java] para marcar ou desmarcar a caixa. Uma vez que o método devolve um valor booleano verdadeiro (ver código Java), a caixa foi marcada,
- a linha 7 do código XHTML gera [4]. Mais uma vez, o método getSelectBooleanCheckbox de [Form.java] é utilizado para gerar o texto [4].
A saída HTML gerada pelo código JSF anterior é a 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>
Em [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. Isto faz com que a caixa de seleção fique marcada.
Agora, abaixo, vamos desmarcar a caixa de seleção [1], enviar o formulário [2] e observar o resultado [3, 4]:
![]() |
Como a caixa de seleção está desmarcada, não é enviado nenhum valor para o campo [1].
O envio do formulário através de [2] fez com que o modelo [Form.java] fosse atualizado pela entrada [1]. O campo selectBooleanCheckbox em [Form.java] recebeu então o valor false. Ao recarregar [index.xhtml], verifica-se que o campo selectBooleanCheckbox no modelo foi efetivamente atualizado [3] e [4]. Vale a pena referir aqui que foi graças ao campo oculto javax.faces.ViewState que o JSF conseguiu determinar que a caixa de seleção inicialmente marcada tinha sido desmarcada pelo utilizador. Com efeito, o valor de uma caixa de seleção desmarcada não está incluído nos valores enviados pelo navegador. Graças à árvore de componentes armazenada no campo oculto javax.faces.ViewState, o JSF determina que havia uma caixa de seleção chamada "selectBooleanCheckbox" no formulário e que o seu valor não está incluído nos valores enviados pelo navegador do cliente. Pode assim 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. Etiqueta <h:selectManyCheckBox>
A tag <h:selectManyCheckBox> gera um grupo de caixas de seleção e, consequentemente, várias tags HTML <input type="checkbox" ...>. Esta tag é o equivalente à tag <h:selectManyListBox>, com a diferença de que os itens selecionáveis são apresentados como caixas de seleção adjacentes, em vez de como uma lista. O que foi dito sobre a tag <h:selectManyListBox> aplica-se também aqui.
Considere o seguinte código JSF:
<!-- line 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 para a tag <h:selectManyCheckbox> na 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 resultante é 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–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 para o servidor se a caixa de seleção estiver marcada,
O modelo para as quatro caixas de seleção é o seguinte campo Java:
private String[] selectManyCheckbox=new String[]{"1","3"};
Esta matriz define:
- quando a página é apresentada, quais as caixas de seleção que devem estar marcadas. Isto é feito através do seu valor, ou seja, do seu campo itemValue. No exemplo acima, as caixas de seleção com valores na matriz {"1","3"} serão marcadas. É isto que se vê na captura de ecrã acima;
- quando a página é enviada, o modelo selectManyCheckbox recebe a matriz de valores das caixas de seleção que o utilizador marcou. É isto que veremos em breve;
- a linha 12 do código XHTML gera [4]. É o seguinte método getSelectManyCheckboxValue que gerou [4]:
public String getSelectManyCheckboxValue(){
return getValue(getSelectManyCheckbox());
}
private String getValue(String[] chaines){
String value="[";
for(String chaine : chaines){
value+=" "+chaine;
}
return value+"]";
}
A saída HTML gerada pelo código JSF anterior é a 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>
Foram geradas quatro tags HTML <input type="checkbox" ...>. As tags nas linhas 3 e 7 têm o atributo checked="checked", o que faz com que apareçam marcadas. Note que todas elas têm o mesmo atributo name="formulaire:selectManyCheckbox"; por outras palavras, os quatro campos HTML têm o mesmo nome. Se as caixas de seleção nas linhas 5 e 9 forem marcadas pelo utilizador, o navegador enviará os valores das quatro caixas de seleção no seguinte formato:
e o modelo para as quatro caixas de seleção
private String[] selectManyCheckbox=new String[]{"1","3"};
receberá a matriz {"2","4"}.
Vamos verificar isto abaixo. Em [1], fazemos a alteração; em [2], enviamos o formulário. Em [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.
Considere o seguinte código JSF:
<!-- line 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 para a tag <h:selectOneRadio> na 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 resultante é 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–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 para o servidor se o botão for selecionado,
O modelo para os quatro botões de opção é o seguinte campo Java:
private String selectOneRadio="2";
Este modelo define:
- quando a página é apresentada, o único botão de opção que deve ser selecionado. Isto é feito através do seu valor, ou seja, do seu campo itemValue. No exemplo acima, o botão de opção com o valor «2» será selecionado. É isto que se vê na captura de ecrã acima;
- quando a página é enviada, o modelo selectOneRadio recebe o valor do botão de opção que foi selecionado. Veremos isto em breve;
- a linha 12 do código XHTML gera [4].
A saída HTML gerada pelo código JSF anterior é a 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 na linha 8 tem o atributo checked="checked", o que faz com que o botão de opção correspondente apareça marcado. Note que todas as tags têm o mesmo atributo name="form:selectOneRadio", o que significa que os quatro campos HTML partilham o mesmo nome. Este é o requisito para um grupo de botões de opção mutuamente 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 de antes:
![]() |
As únicas alterações dizem respeito à forma como os itens da lista para os campos [1] e [2] são gerados. Aqui, são gerados dinamicamente por código Java, enquanto na versão anterior estavam codificados de forma estática na página JSF.
2.6.2. O projeto NetBeans
O projeto NetBeans para a aplicação é o seguinte:
![]() |
O projeto [mv-jsf2-04] é idêntico ao projeto [mv-jsf2-03], com as seguintes diferenças:
- em [1], na página JSF, os itens da lista já não estão codificados de forma rígida no código,
- em [2], o modelo da página JSF [1] será modificado,
- em [3], uma das mensagens será modificada.
2.6.3. A página [index.xhtml] e o seu modelo [Form.java]
A página JSF [index.xhtml] passa a ter o seguinte aspeto:
<?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">
<!-- languages -->
<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">
...
<!-- line 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}"/>
<!-- line 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}"/>
<!-- line 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}"/>
<!-- line 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}"/>
<!-- line 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"/>
...
<!-- line 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}"/>
<!-- line 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 feitas são apresentadas nas linhas 26–28. Onde anteriormente tínhamos 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 isto:
<h:selectOneListbox id="selectOneListBox1" value="#{form.selectOneListBox1}" size="1">
<f:selectItems value="#{form.selectOneListbox1Items}"/>
</h:selectOneListbox>
As três tags <f:selectItem> nas linhas 2–4 foram substituídas pela única tag <f:selectItems> na linha b. Esta tag possui um atributo value cujo valor é uma coleção de elementos do tipo javax.faces.model.SelectItem. Acima, o valor do atributo value é obtido através da chamada ao 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 devolve uma matriz de elementos do tipo javax.faces.model.SelectItem construída pelo método privado getItems na linha 5. Note-se que o método getSelectOneListbox1Items não é o getter de um campo privado denominado selectOneListBox1Items;
- a classe javax.faces.model.SelectItem possui vários construtores.

Na linha 8 do método getItems, utilizamos 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 uma matriz de qte elementos do tipo SelectItem, em que o i-ésimo elemento é obtido através do 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>
é, 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 se aplica a todas as outras listas na página JSF. O modelo [Form.java] inclui agora 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 ficheiro de mensagens
Apenas um ficheiro de mensagens é modificado:
[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 os leitores a testar esta nova versão.
Na maioria das vezes, os elementos dinâmicos de um formulário são o resultado do processamento da lógica de negócio ou provêm de uma base de dados:
![]() |
Vamos examinar o pedido inicial da página JSF [index.xhtml] através de um pedido GET do navegador:
- a página JSF é solicitada [1],
- o controlador [Faces Servlet] solicita a sua exibição em [3]. O motor JSF que processa a página chama o seu modelo [Form.java], por exemplo, o método getSelectOneListBox1Items. Este método poderia muito bem devolver uma matriz de elementos do tipo SelectItem, com base em informações armazenadas numa base de dados. Para tal, chamaria a camada [de negócio] [2b].
2.7. Exemplo mv-jsf2-05: navegação – sessão – tratamento de exceções
2.7.1. A aplicação
A aplicação é a mesma de antes, exceto que o formulário assume agora a forma de um assistente de várias páginas:
![]() |
- em [1], página 1 do formulário — também pode ser acedido através do link 1 em [2]
- em [2], um grupo de 5 links.
- em [3], página 2 do formulário, acedida através do link 2 em [2]
![]() |
![]() |
- em [4], página 3 do formulário, acessado através do link 3 em [2]
- em [5], a página acedida através do link «Levantar uma exceção» em [2]
![]() |
- em [6], a página acessada através do link 4 em [2]. Ela resume as entradas feitas nas páginas 1 a 3.
2.7.2. O projeto NetBeans
O projeto NetBeans para a aplicação é o seguinte:
![]() |
O projeto [mv-jsf2-05] introduz duas novas funcionalidades:
- Em [1], a página JSF [index.xhtml] é dividida em três páginas [form1.xhtml, form2.xhtml, form3.xhtml], pelas quais as entradas foram distribuídas. A página [form4.xhtml] é uma cópia da página [index.xhtml] do projeto anterior. Em [2], a classe [Form.java] permanece inalterada. Servirá de modelo para as quatro páginas JSF acima mencionadas,
- em [3], é adicionada uma página [exception.xhtml]: será utilizada quando ocorrer uma exceção na aplicação.
2.7.3. As páginas [form.xhtml] e o seu modelo [Form.java]
2.7.3.1. O código da página 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"/>
<!-- line 1 -->
<h:outputText value="inputText" styleClass="info"/>
<h:panelGroup>
<h:outputText value="#{msg['form.loginPrompt']}"/>
<h:inputText id="inputText" value="#{form.inputText}"/>
</h:panelGroup>
<!-- line 2 -->
<h:outputText value="inputSecret" styleClass="info"/>
<h:panelGroup>
<h:outputText value="#{msg['form.passwdPrompt']}"/>
<h:inputSecret id="inputSecret" value="#{form.inputSecret}"/>
</h:panelGroup>
<!-- line 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 à seguinte apresentação:
![]() |
Observe os seguintes pontos:
- linha 16: a tabela, que anteriormente tinha três colunas, agora tem apenas duas. A coluna 3, que exibia os valores do modelo, foi removida. Estes valores serão exibidos por [form4.xhtml],
- linhas 40–46: uma tabela com seis links. Os links nas linhas 44 e 46 têm navegação estática: o seu atributo action está codificado de forma rígida. Os outros links têm navegação dinâmica: o seu atributo action aponta para um método no bean do formulário responsável por devolver a chave de navegação. Os métodos referenciados em [Form.java] são os seguintes:
// événements
public String doAction2(){
return "form2";
}
public String doAction4(){
return "form4";
}
public String doAlea(){
// un nombre aléatoire entre 1 et 3
int i=1+(int)(3*Math.random());
// on rend la clé de navigation
return "form"+i;
}
public String throwException() throws java.lang.Exception{
throw new Exception("Exception test");
}
Por enquanto, vamos ignorar o método throwException na linha 17. Voltaremos a ele mais tarde. Os métodos doAction2 e doAction4 simplesmente devolvem a chave de navegação sem realizar qualquer processamento. Poderíamos facilmente 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}"/>
O método doAlea gera uma chave de navegação aleatória cujo valor é selecionado do 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. Ciclo de vida do modelo [Form.java] para as páginas [form*.xhtml]
Considere a seguinte sequência de ações:
![]() |
- em [1], preenchemos a página 1 e passamos para a página 3,
- em [2], preenchemos a página 3 e voltamos à página 1,
![]() |
- em [3], a página 1 é recuperada tal como foi introduzida. Em seguida, voltamos à página 3,
- em [4], a página 3 é encontrada exatamente como foi introduzida.
O mecanismo do campo oculto [javax.faces.ViewState] não é suficiente para explicar este fenómeno.
Ao passar de [1] para [2], ocorrem várias etapas:
- o modelo [Form.java] é atualizado com o POST de [form1.jsp]. Em particular, o campo inputText recebe o valor "outro texto",
- a chave de navegação «form3» aciona a exibição de [form3.xhtml]. O ViewState incorporado em [form3.xhtml] contém apenas o estado dos componentes em [form3.xhtml], não os de [form1.xhtml].
Ao passar de [2] para [3]:
- o modelo [Form.java] é atualizado com o POST de [form3.xhtml]. Se o ciclo de vida do modelo [Form.java] estiver definido como "request", um objeto [Form.java] totalmente novo é criado antes de ser atualizado pelo POST de [form3.xhtml]. Neste caso, o campo inputText do modelo reverte para o seu valor padrão:
private String inputText="texte";
e mantém-no: na verdade, no POST de [form3.xhtml], nada atualiza o campo inputText, que faz parte do modelo [form1.xhtml] e não do [form3.xhtml],
- a tecla de navegação "form1" faz com que [form1.xhtml] seja exibido. A página exibe o seu modelo. No nosso caso, o campo de entrada de login ligado ao modelo inputText exibirá "texto" e não o valor "outro texto" introduzido em [1]. Para que o campo inputText retenha o valor introduzido em [1], o âmbito do modelo [Form.java] deve ser session e não request. Neste caso,
- Após o pedido POST de [form1.xhtml], o modelo será colocado na sessão do cliente. O campo inputText terá o valor «some other text»;
- Quando [form3.xhtml] for enviado via POST, o modelo será recuperado desta sessão e atualizado pelo POST de [form3.xhtml]. O campo inputText não será atualizado por este POST, mas manterá o valor «some other text» adquirido após o POST de [form1.xhtml] [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 âmbito de sessão.
2.7.4. Tratamento de exceções
Vamos rever a arquitetura geral de uma aplicação JSF:
![]() |
O que acontece quando um manipulador de eventos ou um modelo deteta uma exceção proveniente da camada de negócios — como, por exemplo, uma desconexão inesperada de uma base de dados?
- Os manipuladores de eventos [2a] podem interceptar qualquer exceção proveniente da camada [de negócios] e devolver uma chave de navegação ao controlador [Faces Servlet] que aponta para uma página de erro específica para a exceção;
- Para os modelos, esta solução não é viável porque, quando são invocados [3,4], o sistema encontra-se na fase de renderização de uma página XHTML específica e já não na fase de seleção. Como é possível mudar de página enquanto se está na fase de renderização de uma delas? Uma solução simples, embora nem sempre adequada, é não tratar a exceção, que se propagará então até ao contentor de servlets que executa a aplicação. O contentor pode ser configurado para exibir uma página específica quando uma exceção chega até ele. Esta solução é sempre viável, e vamos analisá-la agora.
2.7.4.1. Configurar a aplicação Web para o tratamento de exceções
A configuração de uma aplicação web para o tratamento de exceções é feita no seu ficheiro [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>
As linhas 32–39 contêm a definição de duas páginas de erro. Pode ter quantas tags <error-page> forem necessárias. A tag <location> especifica a página a apresentar em caso de erro. O tipo de erro associado à página pode ser definido de duas formas:
- usando a tag <exception-type>, que define o tipo Java da exceção tratada. Assim, a tag <error-page> nas linhas 36–39 especifica que, se o contêiner de servlets capturar uma exceção do tipo [java.lang.Exception] ou um tipo derivado (linha 37) durante a execução da aplicação, ele deve exibir a página [/faces/exception.xhtml] (linha 38). Ao utilizar aqui o tipo de exceção mais genérico [java.lang.Exception], garantimos que todas as exceções são tratadas,
- através 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 da aplicação, a aplicação não intervém na resposta. É o contentor de servlets que gera esta resposta, enviando uma página de erro predefinida. A primeira linha da resposta HTTP contém um código de erro 404, indicando que a página P solicitada não existe. Poderá querer gerar uma resposta que, por exemplo, siga o guia de estilo visual da aplicação ou forneça links para resolver o problema. Neste caso, utilizaria uma tag <error-page> com uma tag <error-code>404</error-code>.
Acima, o código de erro HTTP 500 é o código devolvido no caso de uma «falha» da aplicação. Este é o código que seria devolvido se uma exceção fosse propagada até ao contentor de servlets. As duas tags <error-page> nas linhas 28–35 são, portanto, provavelmente redundantes. Incluímos ambas para ilustrar as duas formas de lidar com um erro.
2.7.4.2. Simulação da exceção
Uma exceção é acionada artificialmente pelo link [Lançar uma exceção]:
![]() |
![]() |
Ao clicar na ligação [Lançar uma exceção] [1], a página [2] é apresentada.
No código das páginas [formx.xhtml], o link [Lançar uma exceção] é gerado da seguinte forma:
<!-- liens -->
<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, quando o link é clicado, o método [form].throwException será executado. É o seguinte:
public String throwException() throws java.lang.Exception{
throw new Exception("Exception test");
}
Aqui, é lançada uma exceção do tipo [java.lang.Exception]. Esta propagar-se-á até ao contentor de servlets, que, por sua vez, apresentará a página [/faces/exception.xhtml].
2.7.4.3. Informações relacionadas com uma exceção
Quando uma exceção é propagada até ao contentor de servlets, este apresenta a página de erro correspondente, passando-lhe informações sobre a exceção. Estas informações são adicionadas como novos atributos ao pedido que está a ser processado. O pedido do navegador e a resposta que irá receber são encapsulados em objetos Java do tipo [HttpServletRequest request] e [HttpServletResponse response]. Estes objetos estão disponíveis em todas as fases do processamento do pedido 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 será usado para gerar a resposta. Este objeto contém, em particular, o canal TCP/IP a ser usado para o fluxo de resposta HTTP. Todas as camadas t1, t2, ..., tn envolvidas no processamento do objeto de solicitação têm acesso a esses dois objetos. Cada uma delas pode acessar os elementos da solicitação inicial e preparar a resposta, enriquecendo o objeto de resposta. Por exemplo, uma camada de localização pode definir a localidade da resposta usando o método response.setLocale(Locale l).
As várias camadas podem transmitir informações entre si através do objeto de solicitação. Este objeto possui um dicionário de atributos, que está vazio no momento da criação, e que pode ser preenchido pelas camadas de processamento sucessivas. Estas camadas podem inserir as informações necessárias para a camada de processamento seguinte nos atributos do objeto de solicitação. Existem dois métodos para gerir os atributos do objeto de solicitação:
- void setAttribute(String s, Object o), que adiciona um objeto o identificado pela string s aos atributos,
- Object getAttribute(String s), que recupera o atributo o identificado pela string s.
Quando uma exceção é propagada até ao contentor de servlets, o contentor define os seguintes atributos na solicitação que está a ser processada:
key | valor |
o código de erro HTTP que será devolvido ao cliente | |
o tipo Java da exceção, juntamente com a mensagem de erro. | |
o URL solicitado quando a exceção ocorreu | |
o servlet que estava a processar o pedido quando a exceção ocorreu |
Iremos utilizar estes atributos da solicitação na página [exception.xhtml] para os apresentar.
2.7.4.4. A página de erro [ exception.xhtml]
O 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. Expressões na página de exceção
Na cadeia de processamento de pedidos 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 página é convertida num servlet pelo contentor de servlets, ou seja, numa classe Java padrão. Mais especificamente, a página XHTML é convertida em código Java que é executado no âmbito 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, encontrará o código Java correspondente à página XHTML. Este código terá vários objetos inicializados pelo método _jspService na linha 1 acima:
- linha 1: HttpServletRequest request: o pedido atualmente a ser processado,
- 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. Tal como o objeto request, o objeto application pode ter atributos. Estes são partilhados por todas as solicitações de todos os clientes. Geralmente, são atributos de leitura apenas,
- linha 6: HTTPSession session: representa a sessão do cliente. Tal como os objetos request e application, o objeto session pode ter atributos. Estes são partilhados por todas as solicitações do mesmo cliente,
- linha 9: JspWriter out: um fluxo de escrita para o navegador do cliente. Este objeto é útil para depurar uma página XHTML. Qualquer coisa escrita através de out.println(text) será exibida no navegador do cliente.
Ao escrever #{expressão} numa página JSF, a expressão pode ser a chave de um atributo dos objetos request, session ou application mencionados acima. O atributo correspondente é procurado sucessivamente nestes três objetos. Assim, #{chave} é avaliado da seguinte forma:
- request.getAttribute(key)
- session.getAttribute(key)
- application.getAttribute(key)
Assim que for obtido um valor diferente de nulo, a avaliação de #{key} é interrompida. Poderá querer ser mais específico, indicando o contexto em que o atributo deve ser pesquisado:
- #{requestScope['key']} para procurar o atributo no objeto de solicitação,
- #{sessionScope['key']} para procurar o atributo no objeto de sessão,
- #{applicationScope['key']} para procurar o atributo no objeto da aplicação.
Foi isto que foi feito na página [exception.xhtml] na página 116. Os atributos utilizados são os seguintes:
key | domain | valor |
solicitação | Consulte a secção 2.7.4.3. | |
o mesmo | o mesmo | |
o mesmo | igual | |
o mesmo | igual |
As várias mensagens necessárias para a página JSF [exception.xhtml] foram adicionadas aos ficheiros de mensagens 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 da entrada do utilizador
2.8.1. A aplicação
A aplicação apresenta um formulário de entrada. Após a validação, o mesmo formulário é devolvido como resposta, juntamente com quaisquer mensagens de erro, caso a entrada tenha sido considerada incorreta.
![]() |
![]() |
2.8.2. O projeto NetBeans
O projeto NetBeans para a aplicação é o seguinte:
![]() |
O projeto [mv-jsf2-06] baseia-se novamente numa única página [index.html] [1] e no seu modelo [Form.java] [2]. Continua a utilizar mensagens do ficheiro [messages.properties], mas apenas em francês [3]. A opção para alterar o idioma não está disponível.
2.8.3. O ambiente de aplicação
Apresentamos aqui o conteúdo dos ficheiros que configuram a aplicação, sem fornecer explicações específicas. Estes ficheiros ajudam a compreender melhor o que se segue.
<?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. Será explicada mais adiante.
O ficheiro 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 o 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">
<!-- line 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"/>
<!-- line 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}"/>
<!-- line 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}"/>
<!-- line 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}"/>
<!-- line 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}"/>
<!-- line 6 -->
...
<!-- line 7 -->
...
<!-- line 8 -->
...
<!-- line 9 -->
...
<!-- line 10 -->
...
<!-- line 11 -->
...
<!-- line 12 -->
...
<!-- line 13 -->
...
</h:panelGrid>
<!-- control buttons -->
<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 utilização de tags:
- para exibir mensagens de erro <h:messages> (linha 14), <h:message> (linhas 24, 29, 34),
- que impõem restrições de validade à entrada <f:validateLongRange> (linha 39), <f:validateDoubleRange>, <f:validateLength>, <f:validateRegex>,
- que definem um conversor entre a entrada e o seu modelo, como <f:convertDateTime>.
O modelo para esta 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() {
}
// foreclosures
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 = "";
// actions
public String submit() {
...
}
public String cancel() {
...
}
// validators
public void validateSaisie10(FacesContext context, UIComponent component, Object value) {
...
}
// getters and setters
...
}
A novidade aqui é que os campos do modelo já não são apenas do tipo String, mas de vários tipos.
2.8.5. As diferentes entradas do formulário
Vamos agora examinar os diferentes campos do formulário, um por um.
2.8.5.1. Campos 1 a 4: introdução de um número inteiro
A página [index.xhtml] apresenta o campo de entrada 1 da seguinte forma:
<!-- ligne 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 em [Form.java]:
private Integer saisie1 = 0;
Quando o navegador envia um pedido GET, a página [index.xhtml] associada ao seu modelo [Form.java] apresenta o seguinte:
- a linha 2 produz [1],
- a linha 3 produz [2],
- a linha 4 exibe [3],
- a linha 5 exibe [4].
Suponha que a seguinte entrada seja introduzida e enviada:
![]() |
Obtemos então o seguinte resultado no formulário devolvido pela aplicação:
![]() |
- em [1], a entrada incorreta,
- em [2], a mensagem de erro que a indica,
- Em [3], vemos que o valor do campo `Integer` do modelo, `saisie1`, não se alterou.
Vamos explicar o que aconteceu. Para isso, voltemos ao ciclo de processamento de uma página JSF:
![]() |
Analisamos este ciclo para o componente:
<h:inputText id="saisie1" value="#{form.saisie1}" styleClass="saisie"/>
e o seu modelo:
private Integer saisie1 = 0;
- Em [A], a página [index.xhtml] enviada durante o pedido GET do navegador é restaurada. Em [A], a página está exatamente como o utilizador a recebeu. O componente id="saisie1" volta ao seu valor inicial "0",
- em [B], os componentes da página recebem os valores enviados pelo navegador. Em [B], a página está tal como o utilizador a introduziu e validou. O componente id="saisie1" recebe o valor enviado "x",
- em [C], se a página contiver validadores e conversores explícitos, estes são executados. Os conversores implícitos também são executados se o tipo do campo associado ao componente não for do tipo String. É o que acontece aqui, onde o campo form.saisie1 é do tipo Integer. O JSF tentará converter o valor "x" do componente id="saisie1" para um tipo Integer. Isto causará um erro que interromperá o ciclo de processamento [A-F]. Este erro será associado ao componente id="saisie1". Através de [D2], passamos então diretamente para a fase de renderização da resposta. A mesma página [index.xhtml] é devolvida,
- a fase [D] só ocorre se todos os componentes de uma página tiverem passado na fase de conversão/validação. É nesta 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 seguinte é executado novamente:
<!-- ligne 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 apresentada em [2] provém da linha 4 do ficheiro [index.xhtml]. A tag <h:message for="idComposant"/> apresenta a mensagem de erro associada ao componente especificado pelo atributo for, caso ocorra um erro. A mensagem apresentada em [2] é padrão e encontra-se no ficheiro [javax/faces/Messages.properties] dentro do arquivo [jsf-api.jar]:
![]() |
Em [2], podemos ver que o ficheiro de mensagens existe em várias variantes. Vamos examinar o conteúdo de [Messages_fr.properties]:
O ficheiro contém mensagens divididas em categorias:
- erros num componente, linha 3,
- erros de conversão entre um componente e o seu modelo, linha 12
- erros de validação quando existem validadores na página, linha 23.
O erro que ocorreu no componente id="saisie1" é um erro de conversão do tipo String para o tipo Integer. A mensagem de erro associada é a que se encontra na linha 18 do ficheiro 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 que foi exibida é reproduzida abaixo:
![]() |
Podemos ver na mensagem:
- o parâmetro {2} foi substituído pelo ID do componente para o qual ocorreu o erro de conversão,
- o parâmetro {0} foi substituído pelo valor introduzido em [1] para o componente,
- o parâmetro {1} foi substituído pelo número 9346.
A maioria das mensagens relacionadas com componentes tem duas versões: uma versão resumida e uma versão detalhada. É o caso das linhas 16–18:
A mensagem com a chave _detail (linha 2) é a chamada mensagem detalhada. A outra é a chamada mensagem resumida. A tag <h:message> apresenta a mensagem detalhada por predefinição. Este comportamento pode ser alterado utilizando os atributos showSummary e showDetail. É isto que se faz para o componente com o id saisie2:
<!-- ligne 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 2: O componente saisie2 está ligado ao seguinte campo form.saisie2:
private Integer saisie2 = 0;
O resultado obtido é o seguinte:
![]() |
- em [1], a mensagem detalhada; em [2], a mensagem resumida.
A tag <h:messages> apresenta todas as mensagens de erro resumidas de todos os componentes, bem como as mensagens de erro não associadas a um componente específico, sob a forma de uma lista. Mais uma vez, os atributos podem alterar este comportamento predefinido:
- showDetail: true / false para ativar ou desativar mensagens detalhadas,
- showSummary: true / false para exibir ou ocultar mensagens resumidas,
- globalOnly: true / false para especificar se devem ser exibidas apenas mensagens de erro não associadas a componentes. Uma mensagem deste tipo poderia, por exemplo, ser criada pelo programador.
A mensagem de erro associada a uma conversão pode ser modificada de várias formas. Em primeiro lugar, pode instruir a aplicação para utilizar um ficheiro de mensagens diferente. Esta alteração é feita no [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–8 definem um ficheiro de mensagens, mas este não é o utilizado pelas tags <h:message> e <h:messages>. Deve utilizar a tag <message-bundle> na linha 9 para o definir. A linha 9 indica às tags <h:message(s)> que o ficheiro [messages.properties] deve ser pesquisado antes do ficheiro [javax.faces.Messages.properties]. Assim, se adicionarmos as seguintes linhas ao ficheiro [messages_fr.properties]:
# conversions
javax.faces.converter.IntegerConverter.INTEGER=erreur
javax.faces.converter.IntegerConverter.INTEGER_detail=erreur d\u00e9taill\u00e9e
o erro devolvido para os componentes input1 e input2 passa a ser:

Outra forma de modificar a mensagem de erro de conversão é utilizar o atributo converterMessage do componente, como mostrado abaixo para o componente saisie3:
<!-- ligne 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á associado 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 a entrada é obrigatória. O campo não pode permanecer vazio. Um campo é considerado vazio se não contiver caracteres ou se contiver uma sequência de espaços. Mais uma vez, existe uma mensagem predefinida em [javax.faces.Messages.properties]:
O atributo requiredMessage permite substituir esta mensagem padrão. Se o ficheiro [messages.properties] contiver as seguintes mensagens:
...
data.required=Vous devez entrer une donnée
integer.required=Vous devez entrer un nombre entier
pode-se obter o seguinte resultado:
![]() |
ou este:
![]() |
Verificar se uma entrada é realmente um número inteiro nem sempre é suficiente. Por vezes, é necessário verificar se o número introduzido se encontra dentro de um determinado intervalo. Nesses casos, utiliza-se um validador. A entrada n.º 4 fornece um exemplo. O seu código em [index.xhtml] é o seguinte:
<!-- ligne 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 3: O componente saisie4 está vinculado ao seguinte modelo form.saisie4:
private Integer saisie4 = 0;
Linhas 3–5: A tag <h:inputText> tem uma tag filha <f:validateLongRange> que aceita dois atributos opcionais, minimum e maximum. Esta tag, também conhecida como validador, permite adicionar uma restrição ao valor de entrada: este não deve ser apenas um inteiro, mas um inteiro dentro do intervalo [minimum, maximum] se ambos os atributos minimum e maximum estiverem presentes; maior ou igual a minimum se apenas o atributo minimum estiver presente; e menor ou igual a maximum se apenas o atributo maximum estiver presente. O validador <f:validateLongRange> tem mensagens de erro predefinidas em [javax.faces.Messages.properties]:
Mais uma vez, é possível substituir estas mensagens por outras. Existe um atributo validatorMessage que permite definir uma mensagem específica para o componente. Assim, com o seguinte código JSF:
<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: Introdução de um número real
A introdução de números reais segue regras semelhantes às da introdução de números inteiros. O código XHTML para as entradas 5 e 6 é o seguinte:
<!-- ligne 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}"/>
<!-- ligne 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 com os componentes saisie5 e saisie6:
private Double saisie5 = 0.0;
private Double saisie6 = 0.0;
As mensagens de erro associadas aos conversores e validadores para os componentes saisie5 e saisie6, em [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: Introdução de um valor booleano
A introdução de um valor booleano deve normalmente ser feita através de uma caixa de seleção. Se for feita através de um campo de introdução, a cadeia de caracteres «true» é convertida no valor booleano true, e qualquer outra cadeia de caracteres é convertida no valor booleano false.
O código XHTML para o exemplo:
<!-- ligne 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 para o componente saisie7:
private Boolean saisie7 = true;
Aqui está um exemplo de uma entrada e a sua resposta:
![]() |
Em [1], o valor introduzido. Após a conversão, esta cadeia de caracteres «x» torna-se o valor booleano falso. Isto é mostrado em [2]. O valor [3] do modelo não se alterou. Só se altera depois de todas as conversões e validações na página terem sido bem-sucedidas. Não foi esse o caso neste exemplo.
2.8.5.4. Entrada 8: Introdução de uma data
Neste exemplo, uma data é introduzida utilizando o seguinte código XHTML:
<!-- ligne 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 na 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–9 também utiliza um conversor, mas apenas na direção java.util.Date --> java.lang.String.
O conversor <f:convertDateTime> suporta vários atributos, incluindo o atributo pattern, que especifica o formato da cadeia de caracteres a ser convertida numa data ou o formato em que uma data deve ser apresentada.
Quando a página [index.xhtml] é solicitada pela primeira vez, a linha 8 anterior é apresentada da seguinte forma:
Os campos [1] e [2] exibem ambos o valor do modelo form.saisie8:
private Date saisie8 = new Date();
onde saisie8 é definido como a data atual. O conversor utilizado em ambos os casos para apresentar a data é o seguinte:
<f:convertDateTime pattern="dd/MM/yyyy"/>
onde dd (dia) indica o número do dia, MM (mês) o número do mês e yyyy (ano) o ano. Em [1], o conversor é utilizado para a conversão inversa java.lang.String --> java.util.Date. A data introduzida deve, portanto, seguir o formato «dd/MM/yyyy» para ser válida.
Existem mensagens predefinidas para datas inválidas em [javax.faces.Messages.properties]:
que podem ser substituídas pelas suas próprias mensagens. Por 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 seguinte mensagem-chave: saisie8.error:
saisie8.error=8-Vous devez entrer une date valide au format jj/mm/aaaa
Aqui está um exemplo:
![]()
2.8.5.5. Entrada 9: Introdução de uma cadeia de caracteres com comprimento restrito
A entrada 9 mostra como garantir que uma cadeia de caracteres introduzida tenha um número de caracteres dentro de um intervalo especificado:
<!-- ligne 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}"/>
Linha 4: O validador <f:validateLength minimum="4" maximum="4"/> exige que a cadeia de caracteres introduzida tenha exatamente 4 caracteres. Pode utilizar apenas um dos atributos: minimum para um número mínimo de caracteres ou maximum para um número máximo.
O modelo form.saisie9 para o componente saisie9 na linha 3 é o seguinte:
private String saisie9 = "";
Existem mensagens de erro predefinidas para este tipo de validação:
que pode ser substituída utilizando o atributo validatorMessage, conforme mostrado na linha 3 acima. A mensagem para a 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: Introdução de uma sequência que deve seguir um padrão
A entrada 9B mostra como garantir que uma cadeia de caracteres introduzida tenha um número de caracteres dentro de um intervalo:
<!-- ligne 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}"/>
Linha 4: O validador <f:validateRegex pattern="^\s*\d{2}:\d{2}\s*$"/> exige que a cadeia de caracteres introduzida corresponda a um padrão de expressão regular, neste caso: uma sequência de 0 ou mais espaços, 2 dígitos, os dois pontos (:), 2 dígitos e uma sequência de 0 ou mais espaços.
O padrão form.saisie9B do componente saisie9B na linha 3 é o seguinte:
private String saisie9B;
Existem mensagens de erro predefinidas para este tipo de validação:
que pode ser substituída utilizando o atributo validatorMessage, conforme mostrado na linha 3 acima. A mensagem para a 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: Escreva um método de validação específico
Resumindo: o JSF permite verificar, entre os valores introduzidos, a validade de números (inteiros, decimais), datas, comprimento de cadeias de caracteres e se uma entrada está em conformidade com uma expressão regular. O JSF permite adicionar os seus próprios validadores e conversores aos já existentes. Este tópico não é abordado aqui, mas pode consultar [ref2] para obter mais detalhes.
Aqui apresentamos outro método: um que envolve a validação dos dados introduzidos utilizando um método do modelo do formulário. Aqui está um exemplo:
<!-- ligne 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 na linha 3 é o seguinte:
private Integer saisie10 = 0;
Queremos que o número introduzido seja <1 ou >7. Não podemos verificar isso utilizando os validadores básicos do JSF. Por isso, criamos o nosso próprio método de validação para o componente saisie10. Especificamos isso utilizando 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. O 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 a apresentada na linha 1:
- FacesContext context: o contexto de execução da página — fornece acesso a várias informações, incluindo os objetos de pedido HttpServletRequest e de resposta HttpServletResponse,
- UIComponent component: o componente a ser validado. A tag <h:inputText> é representada por um componente UIInput derivado de UIComponent. Aqui, este componente UIInput é passado como segundo parâmetro,
- Object value: o valor introduzido a ser verificado, convertido para o tipo do modelo. É importante compreender aqui que, se a conversão de String para o tipo do modelo falhar, o método de validação não é executado. Quando o método validateSaisie10 é chamado, significa que a conversão de String para Integer foi bem-sucedida. O terceiro parâmetro é, portanto, do tipo Integer.
- Linha 2: O valor introduzido é convertido para o tipo int,
- linha 3: verificamos se o valor introduzido é <1 ou >7. Se for o caso, a validação está concluída. Caso contrário, o validador deve reportar o erro lançando uma ValidatorException.
A classe ValidatorException tem dois construtores:
![]() |
- o construtor [1] recebe uma mensagem de erro do tipo FacesMessage como parâmetro. Este tipo de mensagem é o que é exibido pelas tags <h:messages> e <h:message>,
- O construtor [2] também permite encapsular o objeto Throwable ou a causa derivada do erro.
Precisamos de construir uma mensagem do tipo FacesMessage. Esta classe possui vários construtores:
![]() |
O construtor [1] define as propriedades de um objeto FacesMessage:
- FacesMessage.Severity severity: um nível de gravidade retirado 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; os parâmetros em falta podem ser definidos posteriormente utilizando métodos de definição.
O construtor [1] não permite especificar uma mensagem a partir de um ficheiro de mensagens internacionalizado. Isto é, obviamente, uma pena. David Geary e Cay Horstmann [ref2] abordam esta lacuna no seu livro «Core JavaServer Faces» com a classe utilitária com.corejsf.util.Messages. Esta é a classe utilizada na linha 4 do código Java para criar a mensagem de erro. 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 ficheiro de mensagens sem a extensão .properties, mas com o nome do pacote. Aqui, o nosso primeiro parâmetro poderia ser `messages` para se referir ao ficheiro `[messages.properties]`. Antes de utilizar o ficheiro especificado pelo primeiro parâmetro, `getMessage` tenta utilizar o ficheiro de mensagens da aplicação, caso exista. Assim, se em `[faces-config.xml]` tivermos declarado um ficheiro de mensagens com a tag:
<application>
...
<message-bundle>messages</message-bundle>
</application>
podemos passar null como primeiro parâmetro para o método getMessage. Foi isso que foi feito aqui (ver [web.xml], página 120),
- String resourceId: a chave da mensagem a recuperar do ficheiro de mensagens. Vimos que uma mensagem pode ter tanto uma versão resumida como uma versão detalhada. resourceId é o identificador da versão resumida. A versão detalhada será recuperada automaticamente utilizando 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 produzida pelo método Messages.getMessage inclui tanto a versão resumida como a versão detalhada, caso tenham sido encontradas. Ambas as versões devem estar presentes; caso contrário, é lançada uma [NullPointerException],
- Object[] params: os parâmetros reais da mensagem, caso esta tenha parâmetros formais {0}, {1}, ... Estes 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);
}
}
- Em [4], o FacesMessage é criado utilizando o método estático Messages.getMessage,
- em [5], o nível de gravidade da mensagem é definido,
- em [6], é lançada uma ValidatorException com a mensagem construída anteriormente. O método de validação foi chamado pelo seguinte código XHTML:
<!-- ligne 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}"/>
Linha 3: O método de validação é executado para o componente com o ID "saisie10". Por conseguinte, a mensagem de erro gerada pelo método validateSaisie10 é associada a este componente e é apresentada na linha 4 (atributo for="saisie10"). A versão detalhada é apresentada por predefinição pela tag <h:message>.
Eis 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 que vimos validavam apenas um único componente. E se a validação pretendida envolver vários componentes? É isso que vamos ver agora. No formulário:

queremos que as entradas 11 e 12 sejam dois números inteiros cuja soma seja igual a 10.
O código JSF será o seguinte:
<!-- line 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}"/>
<!-- line 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 introduzido para o componente saisie11 é, de facto, um inteiro. O mesmo se aplica, na linha 11, ao componente saisie12. Para verificar se saisie11 + saisie12 = 10, poderíamos criar um validador específico. Esta é a solução preferida. Mais uma vez, consultaremos [ref2] para saber mais. Aqui, estamos a adotar uma abordagem diferente.
A página [index.xhtml] é validada por um botão [Validate] cujo código JSF é o seguinte:
<!-- boutons de commande -->
<h:panelGrid columns="2">
<h:commandButton value="#{msg['submit']}" action="#{form.submit}"/>
...
</h:panelGrid>
onde a mensagem msg['submit'] é a seguinte:
submit=Valider
Como se pode ver na linha 3, o método form.submit será executado para processar o clique no botão [Validate]. É o seguinte:
// actions
public String submit() {
// latest validations
validateForm();
// we return the same form
return null;
}
// global validations
private void validateForm() {
if ((saisie11 + saisie12) != 10) {
...
}
É importante compreender que, quando o método submit é executado:
- todos os validadores e conversores do formulário foram executados e aprovados,
- os campos no modelo [Form.java] receberam os valores enviados pelo cliente.
Na verdade, vamos rever o ciclo de processamento de um POST JSF:
![]() |
O método submit é um manipulador de eventos. Ele trata do evento de clique no botão [Validate]. Tal como todos os manipuladores de eventos, é executado na fase [E], depois de todos os validadores e conversores terem sido executados e terem sido bem-sucedidos [C] e de o modelo ter sido atualizado com os valores enviados [D]. Por isso, já não há necessidade de lançar exceções do tipo [ValidatorException], como fazíamos anteriormente. Vamos simplesmente reenviar o formulário com mensagens de erro:
![]() |
Em [1], iremos alertar o utilizador e, em [2] e [3], iremos apresentar um indicador de erro. No código JSF, a mensagem [1] será gerada da seguinte forma:
<h:form id="formulaire">
<h:messages globalOnly="true" />
<h:panelGrid columns="4" columnClasses="col1,col2,col3,col4" border="1">
<!-- ligne 1 -->
...
Na linha 2, a tag <h:messages> exibe, por predefinição, a versão resumida das mensagens de erro para todas as entradas inválidas dos componentes do formulário, bem como todas as mensagens de erro não associadas a componentes. O atributo globalOnly="true" limita a exibição a estas últimas.
As mensagens [2] e [3] são apresentadas utilizando simples tags <h:outputText>:
<!-- line 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}"/>
<!-- line 13 -->
...
<h:outputText value="#{form.errorSaisie12}" styleClass="error"/>
...
Linhas 4–7: O componente saisie11 tem duas mensagens de erro possíveis:
- uma indicando uma conversão inválida ou dados em falta. Esta mensagem, gerada pelo próprio JSF, será contida num tipo FacesMessage e apresentada pela tag <h:message> na linha 5,
- a que iremos gerar se input11 + input12 não for igual a 10. Será exibida na linha 6. A mensagem de erro estará contida no modelo form.errorSaisie11.
As duas mensagens correspondem a erros que não podem ocorrer simultaneamente. A verificação input11 + input12 = 10 é realizada no método submit, que só é executado se não houver erros remanescentes no formulário. Quando for executado, o componente input11 já terá sido validado e o seu modelo form.input11 terá recebido o seu valor. A mensagem na linha 5 deixará de ser apresentada. Por outro lado, se a mensagem na linha 5 for apresentada, significa que existe pelo menos um erro remanescente no formulário e o método submit não será executado. A mensagem na linha 6 não será apresentada. Para garantir que as duas mensagens de erro possíveis se encontram na mesma coluna da tabela, foram agrupadas dentro de uma tag <**h:panelGroup**> (linhas 4 e 7).
O método submit é o seguinte:
// actions
public String submit() {
// latest validations
validateForm();
// we return the same form
return null;
}
// global validations
private void validateForm() {
if ((saisie11 + saisie12) != 10) {
// global msg
FacesMessage message = Messages.getMessage(null, "saisies11et12.incorrectes", null);
message.setSeverity(FacesMessage.SEVERITY_ERROR);
FacesContext context = FacesContext.getCurrentInstance();
context.addMessage(null, message);
// field-related msg
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 validações finais,
- linha 11: verificamos se input11 + input12 = 10,
- caso contrário, linhas 13–14, é criada uma FacesMessage com o ID `saisies11et12.incorrectes`. A mensagem é a seguinte:
saisies11et12.incorrectes=La propriété saisie11+saisie12=10 n'est pas vérifiée
- A mensagem construída desta forma é adicionada (linhas 15–16) à lista de mensagens de erro da aplicação. Esta mensagem não está associada a um componente específico. Trata-se de uma mensagem global da aplicação. Será apresentada pela tag <h:messages globalOnly="true"/> mostrada acima,
- Linha 18: Criamos uma nova mensagem FacesMessage com o ID «error.sign». Fica assim:
error.sign="!"
Mencionámos que o método estático [Messages.getMessage] constrói um FacesMessage com uma versão resumida e uma versão detalhada, caso existam. Aqui, existe apenas a versão resumida da mensagem error.sign. Obtemos a versão resumida de uma mensagem m utilizando m.getSummary(). Nas linhas 19 e 20, a versão resumida da mensagem error.sign é colocada nos campos errorSaisie11 e errorSaisie12 do modelo. Estas serão apresentadas pelas seguintes tags JSF:
<h:outputText value="#{form.saisie11}"/>
...
<h:outputText value="#{form.saisie12}"/>
- Linhas 22–23: Se a condição `saisie11 + saisie12 = 10` for verdadeira, então os dois campos `errorSaisie11` e `errorSaisie12` no modelo são limpos, de modo a que qualquer mensagem de erro anterior seja apagada. É importante lembrar aqui que o modelo é mantido entre os pedidos na sessão do cliente.
Eis um exemplo de execução:
![]() |
Observe na coluna [1] que o modelo recebeu os valores enviados, indicando que todas as operações de validação e conversão entre os valores enviados e o modelo foram bem-sucedidas. O manipulador de eventos form.submit, que lida com o clique no botão [Validate], pôde assim ser executado. Foi este manipulador que gerou as mensagens exibidas em [2] e [3]. Podemos ver que o modelo foi atualizado, apesar de o formulário ter sido rejeitado e devolvido ao cliente. Poderá querer que o modelo não seja atualizado num caso como este. De facto, se o utilizador cancelar a atualização utilizando o botão [Cancelar] [4], não será possível regressar ao modelo inicial, a menos que o tenha guardado.
2.8.5.9. Enviar um formulário via POST sem validar a entrada
Considere o formulário acima e suponha que o utilizador, sem se aperceber dos seus erros, queira abandonar o envio do formulário. Nesse caso, utilizará o botão [Cancel] gerado pelo seguinte código JSF:
<!-- boutons de commande -->
<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 [Cancelar] só será executado se o formulário for válido. Foi isso que demonstrámos para o método form.submit associado ao botão [Enviar]. Se o utilizador quiser cancelar o envio do formulário, não há, naturalmente, necessidade de verificar a validade das suas entradas. Este 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 POST do JSF:
![]() |
Os eventos para os componentes de ação <h:commandButton> e <h:commandLink> com o atributo immediate="true" são processados na fase [C], após o que o ciclo JSF prossegue diretamente para a fase [E] para renderizar a 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 clicar no botão [Cancelar] no formulário anterior, é apresentada a seguinte página:
![]() |
- o formulário é exibido novamente porque o manipulador de eventos form.cancel define a chave de navegação como nula. A página [index.xhtml] é, portanto, retornada,
- o modelo [Form.java] foi modificado pelo método form.cancel. Isto reflete-se na coluna [2], que apresenta este modelo,
- enquanto a coluna [3] reflete o valor enviado para os componentes.
Voltemos ao código JSF do componente saisie1 [4];
<!-- ligne 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}"/>
Linha 4: O valor do componente saisie1 está vinculado ao modelo form.saisie1. Isto resulta em várias coisas:
- durante uma solicitação GET para [index.xhtml], o componente saisie1 exibirá o valor do modelo form.saisie1,
- durante uma solicitação POST para [index.xhtml], o valor enviado para o componente saisie1 é atribuído ao modelo form.saisie1 apenas se todas as validações e conversões do formulário forem bem-sucedidas. Independentemente de o modelo ` ` ter sido atualizado pelos valores enviados, se o formulário for submetido na sequência da solicitação POST, os componentes exibem o valor que foi enviado e não o valor do modelo associado a eles. Isto é mostrado na captura de ecrã acima, onde as colunas [2] e [3] não têm os mesmos valores.
2.9. Exemplo mv-jsf2-07: Eventos relacionados com alterações no estado dos componentes JSF
2.9.1. A aplicação
A aplicação demonstra um exemplo de um pedido POST efetuado sem utilizar um botão ou um link. O formulário é o seguinte:
![]() |
O conteúdo de combo2 [2] está ligado ao item selecionado em combo1 [1]. Quando a seleção em [1] é alterada, é enviada uma solicitação POST, durante a qual o conteúdo de combo2 é atualizado para refletir o item selecionado em [1] e, em seguida, o formulário é enviado. Não é realizada qualquer validação durante esta solicitação POST.
2.9.2. O projeto NetBeans
O projeto NetBeans para a aplicação é o seguinte:
![]() |
Existe um único formulário [index.xhtml] com o seu modelo [Form.java].
2.9.3. O ambiente da aplicação
O ficheiro 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">
<!-- headers -->
<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"/>
<!-- line 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}"/>
<!-- line 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}"/>
<!-- line 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>
<!-- control buttons -->
<h:panelGrid columns="2" border="0">
<h:commandButton value="#{msg['submit']}"/>
...
</h:panelGrid>
</h:form>
</h:body>
</html>
A nova funcionalidade encontra-se no código do combo1, linhas 24–26. Surgem novos atributos:
- onchange: atributo HTML — declara uma função ou código JavaScript a ser executado quando o item selecionado em combo1 for alterado. Aqui, o código JavaScript submit() envia o formulário para o servidor,
- valueChangeListener: atributo JSF — especifica o nome do método a ser executado no servidor quando o item selecionado em combo1 muda. No total, são executados dois métodos: 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 conforme o utilizador o preencheu, mas antes das verificações de validação de entrada. O objetivo aqui é preencher a lista combo2 com base no item selecionado na lista combo1, mesmo que existam entradas inválidas noutras partes do formulário. Aqui está um exemplo:
![]() |
- em [1], uma entrada inicial,
- em [2], alteramos o item selecionado em combo1 de A para B.
O resultado é o seguinte:
![]() |
O pedido POST foi enviado. O conteúdo de combo2 [2] foi atualizado para corresponder ao item selecionado em combo1 [1], apesar de a entrada [3] estar incorreta. O atributo immediate=true fez com que o método form.combo1ChangeListener fosse executado antes das verificações de validação. Sem este atributo, não teria sido executado porque o ciclo de processamento teria parado nas verificações de validade devido ao erro em [3].
As mensagens associadas ao formulário são as seguintes em [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 âmbito de [Form.java] está definido como request:
package forms;
...
@ManagedBean
@RequestScoped
public class Form {
Linha 6: Definimos o âmbito do bean como request.
2.9.5. A classe [Form.java]
A classe [Form.java] é a 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() {
}
// form fields
private String combo1="A";
private String combo2="A1";
private Integer saisie1=0;
// working fields
final private String[] combo1Labels={"A","B","C"};
private String combo1Label="A";
private static final Logger logger=Logger.getLogger("forms.Form");
// methods
public SelectItem[] getCombo1Items(){
// init 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(){
// init combo2 as a function of 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;
}
// listeners
public void combo1ChangeListener(ValueChangeEvent event){
// follow-up
logger.info("combo1ChangeListener");
// retrieve the posted value of combo1
combo1Label=(String)event.getNewValue();
// we return the answer because we want to short-circuit the validations
FacesContext.getCurrentInstance().renderResponse();
}
public String raz(){
// follow-up
logger.info("raz");
// raz du formulaire
combo1Label="A";
combo1="A";
combo2="A1";
saisie1=0;
return null;
}
// getters - setters
...
}
Vamos ligar o formulário [index.xhtml] ao seu modelo [Form.java]:
A lista combo1 é gerada pelo seguinte código JSF:
<h:selectOneMenu id="combo1" value="#{form.combo1}" immediate="true" onchange="submit();" valueChangeListener="#{form.combo1ChangeListener}" styleClass="combo">
<f:selectItems value="#{form.combo1Items}"/>
</h:selectOneMenu>
Obtém os seus itens através do método getCombo1Items do seu modelo (linha 2). Este método está definido nas linhas 28–35 do código Java. Gera uma lista de três itens: {"A", "B", "C"}.
A lista combo2 é gerada pelo seguinte código JSF:
<h:selectOneMenu id="combo2" value="#{form.combo2}" styleClass="combo">
<f:selectItems value="#{form.combo2Items}"/>
</h:selectOneMenu>
Obtém os seus elementos através do método getCombo2Items do seu modelo (linha 2). Este método está definido nas linhas 37–44 do código Java. Gera uma lista de cinco elementos {"X1", "X2", "X3", "X4", "X5"}, em que X é o elemento combo1Label da linha 16. Portanto, quando o formulário é gerado inicialmente, a lista combo2 contém os itens {"A1", "A2", "A3", "A4", "A5"}.
Quando o utilizador altera o item selecionado na lista combo1,
- o evento onchange="submit();" será tratado pelo navegador do cliente. O formulário será então enviado para o servidor,
- no lado do servidor, o JSF detectará que o componente combo1 alterou o seu valor. O método combo1ChangeListener nas linhas 47–54 será executado. Um método ValueChangeListener recebe um objeto do tipo javax.faces.event.ValueChangeEvent como parâmetro. Este objeto permite-lhe recuperar os valores antigo e novo do componente que foi alterado utilizando os seguintes métodos:

Aqui, o componente é a lista combo1 do tipo UISelectOne. O seu valor é do tipo String.
- Linha 51 do modelo Java: o novo valor de combo1 é armazenado em combo1Label, que é utilizado para gerar os itens na lista combo2,
- Linha 53: A resposta é enviada. É importante notar aqui que o manipulador combo1ChangeListener é executado com o atributo immediate="true". É, portanto, executado após a árvore de componentes da página ter sido atualizada com os valores enviados e antes do processo de validação desses valores. No entanto, queremos ignorar este processo de validação porque a lista combo2 deve ser atualizada mesmo que ainda existam entradas inválidas noutras partes do formulário. Solicitamos, portanto, que a resposta seja enviada imediatamente, sem passar pela fase de validação de entrada.
- O formulário será enviado de volta exatamente como foi preenchido. No entanto, os itens nas listas combo1 e combo2 não são valores enviados. Serão regenerados através da chamada dos métodos getCombo1Items e getCombo2Items. O último método utilizará então o novo valor de combo1Label definido por combo1ChangeListener, e os itens na lista combo2 serão alterados.
2.9.6. O botão [ Reset]
Queremos que o botão [Reset] restaure o formulário ao seu estado inicial, conforme mostrado abaixo:
![]() |
![]() |
![]() |
Em [1], o formulário antes do botão [Raz] é enviado; em [2], o resultado do envio.
Embora seja funcionalmente simples, lidar com este caso de utilização acaba por ser bastante complexo. Podemos experimentar várias soluções, incluindo a utilizada para o botão [Cancel] no exemplo anterior:
<h:commandButton value="#{msg['raz']}" immediate="true" action="#{form.raz}"/>
onde o método form.raz é o seguinte:
public String raz(){
// raz du formulaire
combo1Label="A";
combo1="A";
combo2="A1";
saisie1=0;
return null;
}
O resultado obtido ao clicar no botão [Limpar] no exemplo anterior é o seguinte:
![]() |
A coluna [1] mostra que o método form.raz foi executado. No entanto, a coluna [1] continua a exibir os valores enviados:
- para combo1, o valor inserido era «B». Este item está, portanto, selecionado na lista,
- para combo2, o valor inserido era "B5". Como resultado da execução de form.raz, os elementos {"B1", ..., "B5"} de combo2 foram alterados para {"A1", ..., "A5"}. O elemento "B5" já não existe e, por isso, não pode ser selecionado. O primeiro elemento da lista é então apresentado,
- para input1, o valor inserido era 10.
Este é o comportamento normal com o atributo immediate="true". Para obter um resultado diferente, deve enviar os valores que pretende ver no novo formulário, mesmo que o utilizador tenha introduzido valores diferentes. Isto é conseguido com um pouco de 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['form'].submit();
}
</script>
...
<h:commandButton value="#{msg['raz']}" onclick='raz()' immediate="true" action="#{form.raz}"/>
- Linha 10: O atributo onclick='raz()' instrui o navegador a executar a função JavaScript raz quando o utilizador clica no botão [Reset].
- linha 3: o valor "A" é atribuído ao elemento HTML denominado 'form:combo1'. Os vários elementos na linha 3 são os seguintes:
- document: a página exibida pelo navegador,
- document.forms: todos os formulários no documento,
- document.forms['form']: o formulário com o atributo name="form",
- document.forms['form'].elements: todos os elementos do formulário com o atributo name="form",
- document.forms['form'].elements['form:combo1']: o elemento do formulário com o atributo name definido como "form:combo1"
- document.forms['form'].elements['form:combo1'].value: valor que será enviado pelo elemento de formulário com o atributo name="form:combo1".
Para encontrar os atributos name dos vários elementos na página apresentada pelo navegador, pode visualizar o seu código-fonte (abaixo com o IE7):
![]() |
<form id="formulaire" name="formulaire" ...>
...
<select id="formulaire:combo1" name="formulaire:combo1" ...>
Com isto explicado, podemos ver que no código JavaScript da função raz:
- a linha 3 garante que o valor enviado para o componente combo1 será a string A,
- a linha 4 garante que o valor enviado para o componente combo2 será a string A1,
- a linha 5 garante que o valor enviado para o componente saisie1 será a string 0.
Assim que isto estiver feito, ocorrerá o envio do formulário (POST), associado a qualquer botão do tipo <h:commandButton> (linha 10). O método form.raz será executado e o formulário será devolvido tal como foi enviado. Obtemos então o seguinte resultado:
![]() |
Este resultado esconde muitas coisas. Os valores «A», «A1» e «0» dos componentes combo1, combo2 e saisie1 são enviados para o servidor. Suponhamos que o valor anterior de combo1 era «B». Nesse caso, há uma alteração no valor do componente combo1 e o método form.combo1ChangeListener também deve ser executado. Temos dois manipuladores de eventos com o atributo immediate="true". Ambos serão executados? Se sim, em que ordem? Apenas um? Se sim, qual?
Para saber mais, criamos registos na aplicação:
package forms;
import java.util.logging.Logger;
...
public class Form {
...
// form fields
private String combo1="A";
private String combo2="A1";
private Integer saisie1=0;
// working fields
final private String[] combo1Labels={"A","B","C"};
private String combo1Label="A";
private static final Logger logger=Logger.getLogger("forms.Form");
// listener
public void combo1ChangeListener(ValueChangeEvent event){
// follow-up
logger.info("combo1ChangeListener");
// retrieve the posted value of combo1
combo1Label=(String)event.getNewValue();
// we return the answer because we want to short-circuit the validations
FacesContext.getCurrentInstance().renderResponse();
}
public String raz(){
// follow-up
logger.info("raz");
// raz du formulaire
combo1Label="A";
combo1="A";
combo2="A1";
saisie1=0;
return null;
}
...
}
- linha 16: é criado um logger. O parâmetro getLogger permite-nos diferenciar as fontes dos registos. Aqui, o logger é chamado forms.Form,
- linha 21: a chamada ao método combo1ChangeListener é registada,
- linha 30: a entrada no método raz é registada.
Que registos são gerados pelo botão [Limpar] ou quando o valor do combo1 muda? Vamos considerar vários cenários:
- Utilizamos o botão [Limpar] enquanto o item selecionado em combo1 é «A». «A» é, portanto, o último valor do componente combo1. Vimos que o botão [Limpar] executa uma função JavaScript que envia o valor «A» para o componente combo1. O componente combo1, portanto, não altera o seu valor. Os registos mostram então que apenas o método form.raz é executado:
- Utilizamos o botão [Limpar] enquanto o item selecionado em combo1 não for «A». O componente combo1 altera, assim, o seu valor: o seu último valor não era «A», e o botão [Limpar] irá defini-lo como «A». Os registos mostram então que são executados dois métodos, pela seguinte ordem: combo1ChangeListener, raz:
![]() |
- Alteramos o valor de combo1 sem utilizar o botão [Raz]. Os registos mostram que apenas o método combo1ChangeListener é executado:
![]() |
2.10. Exemplo mv-jsf2-08: a tag <h:dataTable>
2.10.1. A aplicação
A aplicação apresenta uma lista de pessoas com a opção de as eliminar:
![]() |
- em [1], uma lista de pessoas,
- em [2], os links que permitem eliminá-las.
2.10.2. O projeto NetBeans
O projeto NetBeans para a aplicação é o seguinte:
![]() |
Existe um único formulário [index.xhtml] com o seu modelo [Form.java].
2.10.3. O ambiente da aplicação
O ficheiro 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 ficheiro 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 o seu modelo [Form.java]
Vamos rever a vista 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>
Linha 14: A tag <h:dataTable> utiliza o campo #{form.personnes} como fonte de dados. É o seguinte:
private List<Person> people;
A classe [Person] é a seguinte:
package forms;
public class Personne {
// data
private int id;
private String nom;
private String prénom;
// manufacturers
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);
}
// getter and 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="person" define o nome da variável que representa a pessoa atual dentro da tag <h:dataTable>,
- o atributo headerClass="headers" define o estilo dos cabeçalhos das colunas da tabela,
- o atributo columnClasses="...." define o estilo de cada coluna da tabela.
Vamos examinar uma das colunas da tabela e ver como está construída:
![]() |
O código XHTML para a 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 do apelido da tabela,
- linhas 14–19: a coluna do nome próprio da tabela.
Agora, vamos examinar a coluna do link [Remover]:
![]() |
Esta coluna é gerada pelo seguinte código:
<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 [Remover] é gerado pelas linhas 4–6. Quando o link é clicado, o método [Form].removePerson 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 {
// model
private List<Personne> personnes;
private int personneId;
// manufacturer
public Form() {
// initialization of the list of persons
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() {
// search for the selected person
int i = 0;
for (Personne personne : personnes) {
// current person = selected person?
if (personne.getId() == personneId) {
// delete the current person from the list
personnes.remove(i);
// we're done
break;
} else {
// next person
i++;
}
}
// we test on the same page
return null;
}
// getters and setters
...
}
- linhas 18–24: o construtor inicializa a lista de pessoas da linha 14,
- linha 10: como esta lista deve persistir entre pedidos, o âmbito do bean é a sessão.
Quando o método [removePerson] na linha 26 é executado, o campo na linha 15 já foi inicializado com o ID da pessoa cujo link [Remove] foi clicado:
<h:commandLink value="Retirer" action="#{form.retirerPersonne}">
<f:setPropertyActionListener target="#{form.personneId}" value="#{personne.id}"/>
</h:commandLink>
A tag <f:setPropertyActionListener> permite que as informações sejam transferidas 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 a ser removida da lista de pessoas — é copiado para o campo [Form].personneId através do getter desse campo. Isto é feito antes de o método referenciado pelo atributo action na linha 1 ser executado.
Linhas 26–43: O método [supprimerPersonne] remove a pessoa cujo ID é igual a *personId*.
2.11. Exemplo mv-jsf2-09: estrutura de uma aplicação JSF
2.11.1. A aplicação
A aplicação demonstra como organizar uma aplicação JSF com duas vistas:
![]() |
A aplicação tem duas vistas:
- em [1], página 1,
- em [2], página 2.
Pode navegar entre as duas páginas. O que queremos mostrar aqui é que as páginas 1 e 2 partilham um layout comum, como se pode ver nas imagens acima.
2.11.2. O projeto NetBeans
O projeto NetBeans para a aplicação é o seguinte:
![]() |
A aplicação é composta exclusivamente por páginas XHTML. Não existe nenhum modelo Java associado.
2.11.3. A página [layout.xhtml]
A página [layout.xhtml] define o layout das páginas da aplicação:
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://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 namespace, **ui**. Este namespace contém as tags utilizadas para formatar as páginas de uma aplicação. As tags deste namespace são utilizadas nas linhas 17, 22, 25 e 32.
A página [layout.xhtml] apresenta informações numa tabela HTML (linha 14). Pode aceder a esta página utilizando um navegador:
![]() |
- em [1], o URL solicitado.
A área [2] foi gerada pelo seguinte código XHTML:
<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> na linha 6 permite que código XHTML externo seja incluído na página. O ficheiro [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–8 será inserido em [layout.xhtml]. Assim, as tags <html> e <body> serão inseridas dentro de uma tag <td>. Isto não causa quaisquer erros. Portanto, as páginas incluídas através de <ui:include> são páginas XHTML completas. Visualmente, apenas a linha 6 terá efeito. As tags <html> e <body> estão presentes por razões sintáticas.
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> na linha 5 inclui o seguinte ficheiro [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 seguinte código XHTML:
<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> na linha 6 inclui o seguinte ficheiro [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 seguinte código XHTML:
<h:form id="formulaire">
...
<td>
<ui:insert name="contenu" >
<h2>Contenu</h2>
</ui:insert>
</td>
...
</table>
</h:form>
A tag <ui:insert> na linha 5 define uma área chamada «content». Esta é uma área que pode conter conteúdo variável. Veremos como. Quando solicitámos a página [layout.xhtml], não foi definido qualquer conteúdo para a área chamada «content». Neste caso, é utilizado o conteúdo da tag <ui:insert> nas linhas 4–6. A linha 5 é, portanto, apresentada.
2.11.4. A página [page1.xhtml]
A página [layout.xhtml] não se destina a ser visualizada. Serve como modelo para as páginas [page1.xhtml] e [page2.xhtml]. Isto é designado por modelo de página. 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>
- A linha 6 utiliza o namespace ui;
- na linha 7, especificamos que a página está associada ao modelo [layout.xhtml] utilizando uma tag <ui:composition>,
- na linha 8, esta associação garante que cada tag <ui:define> será associada a uma tag <ui:insert> no modelo que está a ser utilizado, neste caso [layout.xhtml]. A ligação é estabelecida através do atributo name de ambas as tags. Estas 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, é 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> em [layout.xml].
A página [page2.xhtml] é semelhante à [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. Conclusão
O estudo do JSF 2 aqui apresentado está longe de ser exaustivo. No entanto, é suficiente para compreender os exemplos que se seguem. Para mais informações, consulte [ref2].
2.13. Testes com o Eclipse
Vamos mostrar como testar projetos Maven com o SpringSource Tool Suite:
![]() |
- Em [1], importe um projeto Maven [2] clicando no botão [3]. Aqui, usamos o projeto Maven [mv-jsf2-09] para o Eclipse
- Em [4], o projeto importado foi corretamente reconhecido como um projeto Maven [5],
![]() |
- em [6], o projeto é importado para o explorador de projetos,
- Em [7], executamo-lo num servidor Tomcat [8] [9],
![]() |
- em [10], o Tomcat 7 foi iniciado,
- em [11], a página inicial do projeto [mv-jsf2-09] [11] é apresentada num navegador dentro do Eclipse.

























































































































































