JSF com MyFaces e Tomahawk
Aprenda a utilizar os recursos do MyFaces
F
RANCISCOC
ALAÇAX
AVIERUma das implementações do JavaServer Faces mais utilizada atualmente é o MyFaces, um projeto
da Apache Software Foundation que vem crescendo rapidamente e hoje vai muito além da
especificação JSF. Além do MyFaces Core, que é a implementação do JSF em si, há vários
subprojetos, que fornecem componentes adicionais, trazendo suporte a Ajax, vinculação com dados,
entre outras funcionalidades importantes no desenvolvimento web.
Neste artigo apresentaremos o Apache MyFaces, e criaremos um exemplo que faz uso dos
componentes JSF fornecidos pelo mais popular dos seus subprojetos, o Tomahawk.
Projetos do MyFaces
Muitas das funcionalidades que diferenciam o MyFaces estão nos seus subprojetos.
Tomahawk – Principal e mais utilizado subprojeto do MyFaces. Possui uma grande quantidade
de componentes visuais como editores html, menus, tabelas com ordenação etc. Já há versões
estáveis disponíveis para uso em produção.
ADF Faces / Trinidad – O ADF Faces é a implementação JSF e conjunto de componentes
desenvolvido originalmente pela Oracle e doado para o Projeto MyFaces. Possui suporte a Ajax
e futuramente será chamado Trinidad.
Tobago – Além de ser um conjunto de componentes JSF que funcionam sobre a implementação
do MyFaces, o principal objetivo do Tobago é tornar o desenvolvimento de aplicações web mais
ágil. O Tobago fornece um gerenciador de layouts que organiza automaticamente os
componentes na página, não sendo necessário o uso de tabelas para esse objetivo.
Sandbox – O projeto Sandbox consiste em componentes de teste do MyFaces, ainda em fase de
desenvolvimento e que futuramente podem fazer parte do Tomahawk. Alguns componentes
possuem suporte ao Ajax. Considerado instável pela Apache Software Foundation por se tratar
de componentes de testes.
A aplicação de exemplo
Para ilustrar o uso do MyFaces, construiremos uma agenda de contatos, conforme ilustrado na
Figura 1. O exemplo utiliza vários componentes padrão do JSF (fornecidos pelo MyFaces Core) e
também vários outros do Tomahawk.
Observe que na inclusão de novos contatos, é usado um componente de calendário para informar a
data de aniversário, conforme mostra a Figura 2. Note que usamos também um componente de
menu. Com o Tomahawk, é possível construir menus com submenus de forma rápida. Outra
funcionalidade interessante é a ordenação de colunas. Através de cliques nos títulos das colunas da
tabela de contatos é possível ordenálas conforme desejado. E à medida que um contato marcado
como favorito é adicionado, este aparece no menu Favoritos.
Figura 1. A aplicação de exemplo em ação
Para mantermos o foco na programação JSF, não será feito acesso a banco de dados; todas as
informações dos contatos permanecerão na memória. Na Listagem 1 está o código da entidade
Contato
. Na Listagem 2, temos o código da classe ContatoDao que é responsável pela
inclusão e consulta de contatos. Observe que temos nessa classe o atributo List contatos que é
static
, para que todos os objetos criados a partir desta classe utilizem esta mesma lista (estamos
aplicando o pattern Singleton). O método consultarFavoritos() retorna todos os contatos
que possuem a propriedade favoritos=true. O método consultarAniversariantes()
retorna os contatos que fazem aniversário no dia e mês fornecidos como parâmetros.
Figura 2. Inclusão de contatos
Adicionando suporte ao MyFaces Tomahawk
•
Para que o MyFaces Tomahawk funcione adequadamente, é necessário utilizar uma
implementação JSF. Em nossos exemplos estaremos utilizando o MyFaces como
implementação JSF. Nada impede ao leitor de utilizar os componentes do Tomahawk junto
com a implementação de referência da Sun para o JSF. Até o momento de escrita deste
artigo a versão do Tomahawk que fornece suporte a outras implementações era a 1.1.3. O
leitor pode acompanhar isto no site myfaces.apache.org.
Devese
também
incluir
o
filtro
org.apache.myfaces.component.html.util.ExtensionsFilter
no web.xml:
<filter> <filtername>extensionsFilter</filtername> <filterclass> org.apache.myfaces.component.html.util.ExtensionsFilter </filterclass> </filter> <filtermapping> <filtername>extensionsFilter</filtername> <urlpattern>*.faces</urlpattern> </filtermapping> <filtermapping> <filtername>extensionsFilter</filtername> <urlpattern>/faces/*</urlpattern> </filtermapping>O arquivo completo do web.xml pode ser verificado na Listagem 3.
Para a utilização dos componentes do tomahawk é necessário também o uso da taglib:
<%@ taglib uri="http://myfaces.apache.org/tomahawk" prefix="t"%>Componentes web do exemplo
Descreveremos agora os componentes utilizados em nossa aplicação. Veja também o quadro
“Mais componentes do Tomahawk”.
Menu
Para menus, utilizamos a tag <t:jscookMenu> com o atributo layout="hbr". Isso faz
com que o menu seja renderizado horizontalmente (para um menu vertical utilizase “vbr”).
Usamos também theme="ThemeOffice" para que o menu pareça como os do Microsoft
Office. Os temas disponíveis são ThemeIE, ThemeMiniBlack, ThemeOffice e
ThemePanel
.
Para criar um menu devese utilizar a tag <t:navigationMenuItem>, definindo o nome do
menu no atributo itemLabel. Veja um exemplo na Listagem 4, os menus estão em negrito.
Observe que os submenus são construídos aninhando as tags <t:navigationMenuItem>.
O resultado deste menu está Figura 3:
Figura 3. Exemplo de um menu gerado pelo Tomahawk.
Calendário
O calendário usado no exemplo é criado com a tag <t:inputCalendar>. Fizemos
renderAsPopup="true"
para que apareça um botão que, ao ser clicado, mostra o
calendário, e renderPopupButtonAsImage="true" para que esse “botão popup”
possua uma imagem indicativa do que será mostrado. A aplicação desta tag pode ser vista na
Listagem 5 com destaque em negrito para a utilização desta tag.
Tabela ordenada
O Tomahawk possui uma versão da tag <h:dataTable> do JSF: <t:dataTable> que
possui mais funcionalidades que a tag da implementação de referencia do JSF. Dentre estas
funcionalidades podemos citar: ordenação automática de colunas, suporte a eventos java script para
as linhas, exibição de tabelas em modo news paper (como um jornal) dentre outras. Em nosso
exemplo, para que seja ativada a ordenação automática, foi necessário utilizar o atributo
sortable="true"
. Também é feita a substituição das tag’s <h:column> por
<t:column>
. O uso dessas tag’s é demonstrado na Listagem 6.
O restante do projeto
O restante dos arquivos do nosso projeto de exemplo é o Managed Bean, denominado
Listagem 8. A classe GerenciadorContato possui apenas uma novidade em relação ao JSF
padrão: a utilização da classe NavigationMenuItem, no método getFavoritos(), para
inclusão dinâmica de contatos no menu Favoritos.
Conclusões
Como vimos neste e em vários outros artigos da Java Magazine, o uso de JavaServer Faces torna o
desenvolvimento web mais fácil e as aplicações mais ricas e interativas (por exemplo, veja a
segunda parte da série “Aplicação Completa Java EE”, na Edição 45). A utilização de
implementações como o Apache MyFaces, aliada ao uso de projetos como o Tomahawk ou o
Trinidad, completa esta facilidade. Estes projetos trazem para a programação JSF recursos que antes
só eram possíveis no mundo das aplicações desktop. O Tomahawk, conforme vimos no exemplo,
possui componentes sofisticados que aumentam a qualidade e a navegabilidade das aplicações web.
Mais componentes do Tomahawk
Aqui exploramos alguns componentes e funcionalidades adicionais do Tomahawk.
Validação
Existem vários validadores no Tomahawk. O validador de endereços de email
(<t:validateEmail>) verifica se um texto informado é um email válido ou não (a
validade é apenas sintática). Não sendo válido, é enviada como mensagem de erro a
string definida no atributo detailMessage. Veja um exemplo, onde o campo a ser
validado é um <h:inputText>:
<h:inputText>
<t:validateEmail detailMessage="Não é um email válido."/>
</h:inputText>
O validar de números de cartão de crédito verifica se a quantidade e a estrutura dos
números informados para o cartão de credito é válida ou não. Veja um exemplo de uso:
<h:inputText>
<t:validateCreditCard detailMessage='
#{"{0} Não é um cartão de crédito válido."}'/>
</h:inputText>
Não existindo um validador apropriado, é possível utilizar expressões regulares para
definir a regra de validação desejada. Por exemplo:
<h:inputText>
<t:validateRegExpr pattern='\d{5}'
detailMessage='#{"{0} Campo inválido." }'/>
</h:inputText>
Painel com abas
Os painéis com abas são úteis em formulários web com muitos campos de entrada de
dados, conforme pode ser visto na Figura Q1.
Figura Q1. Exemplo de painel com abas
Para a definição de um painel com abas são necessárias duas tags:
<t:panelTabbedPane>
para a criação da região onde serão renderizadas as abas e
<t:panelTab label="Nome da aba">
para a aba. Por exemplo:
<t:panelTabbedPane>
<t:panelTab label="Tab 1">
<! – conteúdo da aba Tab1 -->
</t:panelTab >
<t:panelTab label="Tab 2">
<! – conteúdo da aba Tab1 -->
</t:panelTab >
</t:panelTabbedPane>
Árvore
O Tomahawk possui também o recurso de árvore de dados ou data tree view, conforme
ilustra a
Figura Q2
que foi extraída do site de exemplos:
irian.at/myfaces/tree2HideRoot.jsf.
A tag <t:tree2 clientSideToggle="false" value="#{managedBean.treeData}">
renderiza uma árvore e a Listagem 9 mostra o código do método getTreeData(), também
extraído do site de exemplos. Este código é responsável por montar a estrutura de dados
da árvore. Observe o uso da classe TreeNodeBase. Esta classe possui um construtor
que recebe três parâmetros: um identificador para o nodo, o nome do nodo e um
booleano que indentifica se o nodo será renderizado como uma folha (true) ou como uma
pasta (false).Note que estando o atributo clienteSideToggle da tag <t:tree2> ajustado
como false a cada clique em um nó da arvore será feita uma nova requisição para busca
dos dados pois os dados continuarão no servidor. Estando este atributo ajustado como
true
toda a árvore é trazida para o cliente e a cada clique em um nó não será feita uma
requisição, pois os dados já estão no cliente (note que isto não é AJAX. Apesar do AJAX
não realizar requisições, neste caso os dados são trazidos para o browser e implica a não
necessidade de consultas ao servidor). O site wiki.apache.org/myfaces/Tree2 fornece
mais informações sobre as opções possíveis para árvores.
Html Editor
Um dos componentes mais interessantes e sofisticados do Tomahawk é o editor de
HTML ilustrado na Figura Q3.
Figura Q3. Exemplo do componente HtmlEditor
O editor possui recursos dignos de qualquer editor de texto básico como negrito,
sublinhado, cores de fontes etc. O seu uso é bem simples. Basta utilizar a tag
<t:inputHtml> e será renderizado o editor de textos.
Outros componentes
Você pode obter mais informações sobre outros componentes do Tomahawk no site
irian.at/myfaces/home.jsf. Tratase de um site que hospeda os exemplos compilados do
Tomahawk e do SandBox. Estes exemplos podem ser baixados, com o código fonte, a
partir do site people.apache.org/builds/myfaces/nightly.
Listagem 1. Classe Contato package br.com.jm.agenda; import java.util.Date; public class Contato implements Comparable<Contato> { private int codigo; private String nome; private String telefone; private Date aniversario; private boolean favorito; //... getters e setters omitidos ... }
Listagem 2. Classe ContatoDao
package br.com.jm.agenda; import java.util.*; public class ContatoDao { private static List<Contato> contatos = new ArrayList<Contato>(); public void incluir(Contato contato) { contatos.add(contato); } public List<Contato> consultar() { return contatos; } public List<Contato> consultarFavoritos() { List<Contato> resultado = new ArrayList<Contato>(); for (Contato contato : contatos) { if (contato.isFavorito()) { resultado.add(contato); } } return resultado; } public List<Contato> consultarAniversanriantes(Date data) { Calendar cal = Calendar.getInstance(); cal.setTime(data); int dia = cal.get(Calendar.DAY_OF_MONTH); int mes = cal.get(Calendar.MONTH); List<Contato> resultado = new ArrayList<Contato>(); for (Contato cont : contatos) { Calendar calAniversario = Calendar.getInstance(); calAniversario.setTime(cont.getAniversario()); int diaAniversario = calAniversario.get(Calendar.DAY_OF_MONTH);
int mesAniversario = calAniversario.get(Calendar.MONTH); if (diaAniversario == dia && mesAniversario == mes) { resultado.add(cont); } } return resultado; } }
Listagem 3 web.xml completo
<webapp id="WebApp_ID"> <displayname>agenda</displayname> <filter> <filtername>extensionsFilter</filtername> <filterclass> org.apache.myfaces.component.html.util.ExtensionsFilter </filterclass> </filter> <filtermapping> <filtername>extensionsFilter</filtername> <urlpattern>*.faces</urlpattern> </filtermapping> <filtermapping> <filtername>extensionsFilter</filtername> <urlpattern>/faces/*</urlpattern> </filtermapping> <servlet> <servletname>Faces Servlet</servletname> <servletclass>javax.faces.webapp.FacesServlet</servletclass> <loadonstartup>1</loadonstartup> </servlet> <servletmapping> <servletname>Faces Servlet</servletname> <urlpattern>*.faces</urlpattern> </servletmapping> </webapp> Listagem 4 menu.jsp <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f"%> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h"%> <%@ taglib uri="http://myfaces.apache.org/tomahawk" prefix="t"%> <h:outputText styleClass="tituloAplicacao" value="Agenda de Contatos" /> <h:panelGrid width="500px" style="backgroundcolor: #cfc"> <t:j s c o o k M e n u layou t = " h b r " them e = " T h e m e O f f i c e " > <t:n a v i g a t i o n M e n u I t e m item L a b e l = " C a d a s t r o " > <t:n a v i g a t i o n M e n u I t e m item L a b e l = " I n c l u i r Conta t o " acti o n = " i n c l u i r " /> <t:n a v i g a t i o n M e n u I t e m item L a b e l = " C o n s u l t a r " > <t:n a v i g a t i o n M e n u I t e m item L a b e l = " C o n s u l t a r Todo s " action="consultar" actionListener= "#{gerenciadorContato.consultar}" /> <t:navigationMenuItem itemLabel="Consultar Aniversariantes de Hoje" action="consultar" actionListener= "#{gerenciadorContato.consultarAniversariantes}" /> </t:navigationMenuItem> </t:navigationMenuItem> <t:navigationMenuItem itemLabel="Favoritos"> <t:navigationMenuItems value="#{gerenciadorContato.favoritos}" /> </t:navigationMenuItem> </t:jscookMenu>
</h:panelGrid> <f:verbatim> <br />
<br />
<br /> </f:verbatim> Listagem 5. incluir.jsp <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f"%> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h"%> <%@ taglib uri="http://myfaces.apache.org/tomahawk" prefix="t"%> <html> <head> <link href="estilo.css" type="text/css" rel="stylesheet" /> <title></title> </head> <body bgcolor="#ffffff"> <f:view> <h:form> <%@ include file="menu.jsp"%> <h:outputText styleClass="titulo" value="Inclusão de Contatos:" /> <h:panelGrid columns="2"> <h:outputText value="Nome:" /> <h:inputText value="#{gerenciadorContato.contato.nome}" /> <h:outputText value="Telefone:" /> <h:inputText value="#{gerenciadorContato.contato.telefone}" /> <h:outputText value="Favorito:" /> <h:selectBooleanCheckbox value="#{gerenciadorContato.contato.favorito}" /> <h:outputText value="Aniversario:" /> <t:i n p u t C a l e n d a r rende r A s P o p u p = " t r u e " rend e r P o p u p B u t t o n A s I m a g e = " t r u e " valu e = " # { g e r e n c i a d o r C o n t a t o . c o n t a t o . a n i v e r s a r i o } " /> <h:commandButton actionListener="#{gerenciadorContato.incluir}" value="Incluir" /> </h:panelGrid> </h:form> </f:view> </body> </html> Listagem 6. consultar.jsp <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f"%> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h"%> <%@ taglib uri="http://myfaces.apache.org/tomahawk" prefix="t"%> <html> <head> <link href="estilo.css" type="text/css" rel="stylesheet" /> <title></title> </head> <body bgcolor="#ffffff"> <f:view> <h:form> <%@ include file="menu.jsp"%> <h:outputText styleClass="titulo" value="#{gerenciadorContato.tituloTela}" /> <t:dataTable cellspacing="0" headerClass="tituloTabela" rowClasses="linha1, linha2" columnClasses="colE, colC, colC, colC" width="500px" sortable="true" value="#{gerenciadorContato.contatos}" var="contato"> <t:column> <f:facet name="header"> <h:outputText value="Nome" /> </f:facet> <h:outputText value="#{contato.nome}" /> </t:column> <t:column><f:facet name="header"> <h:outputText value="Telefone" /> </f:facet> <h:outputText value="#{contato.telefone}" /> </t:column> <t:column> <f:facet name="header"> <h:outputText value="Favoritos" /> </f:facet> <h:outputText rendered="#{contato.favorito}" value="sim" /> <h:outputText rendered="#{!contato.favorito}" value="não" /> </t:column> <t:column> <f:facet name="header"> <h:outputText value="Aniversário" /> </f:facet> <h:outputText value="#{contato.aniversario}"> <f:convertDateTime pattern="dd/MM/yyyy" /> </h:outputText> </t:column> </t:dataTable> </h:form> </f:view> </body> </html>
Listagem 7. Classe GerenciadorContato
package br.com.jm.agenda; import java.util.*; import javax.faces.event.ActionEvent; import javax.faces.model.*; import org.apache.myfaces.custom.navmenu.NavigationMenuItem; public class GerenciadorContato { private Contato contato = new Contato(); private DataModel contatos; private String tituloTela; public List getFavoritos() { List resultado = new ArrayList(); ContatoDao cDao = new ContatoDao(); List<Contato> favoritos = cDao.consultarFavoritos(); for (Contato cont : favoritos) { StringBuilder label = new StringBuilder(cont.getNome()); label.append(": "); label.append(cont.getTelefone()); resultado.add(new NavigationMenuItem(label.toString(), "")); } return resultado; } public void consultarAniversariantes(ActionEvent e){ tituloTela = "Aniversariantes de Hoje"; ContatoDao cDao = new ContatoDao(); contatos = new ListDataModel(cDao.consultarAniversanriantes(new Date())); } public void consultar(ActionEvent e){ tituloTela = "Contatos da Agenda"; ContatoDao cDao = new ContatoDao(); contatos = new ListDataModel(cDao.consultar()); } public void incluir(ActionEvent e) { ContatoDao cDao = new ContatoDao(); cDao.incluir(contato); contato = new Contato(); }
//... getters e setters omitidos } Listagem 8 faces-config.xml <facesconfig> <managedbean> <managedbeanname>gerenciadorContato</managedbeanname> <managedbeanclass> br.com.jm.agenda.GerenciadorContato </managedbeanclass> <managedbeanscope>session</managedbeanscope> </managedbean> <navigationrule> <fromviewid>*</fromviewid> <navigationcase> <fromoutcome>incluir</fromoutcome> <toviewid>/incluir.jsp</toviewid> </navigationcase> <navigationcase> <fromoutcome>consultar</fromoutcome> <toviewid>/consultar.jsp</toviewid> </navigationcase> </navigationrule> </facesconfig>
Listagem 9 exemplo do código Java necessário para montar uma tree
public TreeNode getTreeData() { TreeNode treeData = new TreeNodeBase(); //adicionado a pasta Frank Foo TreeNodeBase personNode = new TreeNodeBase("", "Frank Foo", false); personNode.getChildren().add(new TreeNodeBase("", "Requires Foo", false)); TreeNodeBase folderNode = new TreeNodeBase("", "Requires Foo Reviewer", false); personNode.getChildren().add(folderNode); personNode.getChildren().add(new TreeNodeBase("", "Requires Foo Recommendation", false)); folderNode = new TreeNodeBase("", "Requires Foo Approval", false); personNode.getChildren().add(folderNode); folderNode = new TreeNodeBase("", "Requires Bar Processing", false); personNode.getChildren().add(folderNode); folderNode = new TreeNodeBase("", "Requires Bar Approval", false); personNode.getChildren().add(folderNode); treeData.getChildren().add(personNode); //adicionado a pasta Betty Bar personNode = new TreeNodeBase("", "Betty Bar", false); return treeData; }