21 de agosto de 2007

Tutorial Facelets

Olá a todos, este é o primeiro tutorial que aborda o assunto de Facelets aqui no JavaWora. Acredito que todos que gostam de Java Server Faces estão super acostumados com a tecnologia, mas aqueles que usavam Struts e html puro, uma vez no mundo do JSF, uma das principais dificuldades (contornáveis é claro) era compreender a lógica das tags que de certa forma se distância do HTML convencional, o que eu até discordo em partes, afinal acho que um <html:text/> não se aproxima tanto de <input type=”text”/>, mas tudo bem, sabemos que da mesma forma aprender Struts não é uma tarefa das mais fáceis. Enfim vamos ao que interessa.

Um dos pontos que mais desagrada empresas que vão utilizar JSF é a distância do HTML convencional, portanto uma dificuldade de que designers possam efetuar atualizações, manutenções e coisas do gênero em uma página, pois bem, para “simplificar” toda essa dificuldade, foi desenvolvido uma tecnologia chamada de Facelets, aqueles que já viram o Tapestry verão muitas semelhanças, apontada como uma das inspiradoras para o Facelets.
No Tapestry utilizávamos a palavra chave jwcid (Java Web Component Id), para que o Framework substituísse aquele conteúdo por um componente desejado, e eis que apresento a palavra mágica do Facelets: jsfc (Java Server Faces Component). Não se engane isso não é uma cópia descarada, mas uma inspiração do primo de JSF, mas qual a mágica de tal palavrinha no caso de Facelets?O que ganhamos com isso?

Imagine o seguinte, você recebe a pagina HTML do designer, e você tem que alterar tudo aquilo, porque é tudo diferente do que você usa. Seus problemas acabaram! Basta você por uma palavrinha mágica em componentes, por exemplo:

<input type=”text”/>
Adicione:

<input type=”text” jsfc=”h:inputText” value=”#{cadastro.nome}”/>

Mágico, não acham? Um reaproveitamento de código maravilhoso e louvável. Outro ponto que nos interessa em reaproveitamento é a criação de um template, tiles é um bom framework mas deixa a desejar em alguns pontos. Com Facelets podemos definir um template declarativamente, de forma que a página sabe em qual template está inserida, reduzindo o esforço para descobrir seus componentes.
Para criar um template apenas devemos assinar os componentes da página por meio de <ui:insert name="menu"> dentre as tags do facelets, caso queiramos inserir o conteúdo de um html naquele tag na página que define o template, utilizamos então: <ui:include src="/templates/menu.xhtml"/>. No momento em que vamos utilizar um determinado template numa página, basta declará-lo: <ui:composition template="/templates/common.xhtml">, e inserir o que estmos sobrescrevendo por meio de <ui:define name="body">conteúdo</ui:define> no espaço delimitado pelas tags em conteúdo. Mais uma vez, simples, muito simples. O Facelets ainda possui componentização, Ájax integrado, dentre outras funcionalidades, mas vamos nos focar no core aqui, que é templates e reaproveitamento de html original.
O suporte de IDES ao Facelets ainda é fraco, o NetBeans possui um suporte, já no universo eclipse, temos o Exadel e agora o novíssimo Red Hat Developer Studio, os melhores na minha opinião. O exemplo deste tutorial foi desenvolvido com a implementação da Sun e no tomcat 5.5, no tomcat 6 algumas mudanças são necessárias. Os jars necessários são:

  • common-annotations.jar
  • commons-beanutils.jar
  • commons-collections.jar
  • commons-digester.jar
  • commons-logging.jar
  • el-api.jar
  • el-ri.jar
  • jsf-api.jar
  • jsf-facelets.jar
  • jsf-impl.jar
  • jsf-tlds.jar
  • jstl.jar
  • standard.jar





O web-xml necessário está na Figura 1. Criaremos três Classes para nossa lógica, são elas:

Listagem 1 – Login.java

package br.com.facelets.bean;
public class Login {
private String userName;
private String senha;
public Login() {
}
//sets e gets omitidos
@Override
public int hashCode() {
final int PRIME = 31;
int result = 1;
result = PRIME * result + ((senha == null) ? 0 : senha.hashCode());
result = PRIME * result + ((userName == null) ? 0 : userName.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
final Login other = (Login) obj;
if (senha == null) {
if (other.senha != null)
return false;
} else if (!senha.equals(other.senha))
return false;
if (userName == null) {
if (other.userName != null)
return false;
} else if (!userName.equals(other.userName))
return false;
return true;
}
}



Listagem 2 – Cadastro.java

package br.com.facelets.bean;
import javax.faces.context.FacesContext;
import br.com.facelets.business.CadastroSaver;
public class Cadastro {
private String nome;
private String senha;
private String userName;
public String getLogin() {
return userName;
}
public void setLogin(String login) {
this.userName = login;
}
//sets e gets omitidos
public String salvar(){
Login login = new Login();
login.setUserName(userName);
login.setSenha(senha);
CadastroSaver.salvar(login, this);
return "ok";
}
public String logar(){
Login login = new Login();
login.setUserName(userName);
login.setSenha(senha);
Cadastro cadastro = CadastroSaver.busca(login);
if(cadastro!=null){
FacesContext.getCurrentInstance().getExternalContext()
.getSessionMap().put("user", cadastro);
return "ok";
}else{
FacesContext.getCurrentInstance().getExternalContext()
.getRequestMap().put("erro", "login/senha inválidos");
return "erro";
}
}
}



Listagem 3 – CadastroSaver.java

package br.com.facelets.business;
import java.util.Hashtable;
import br.com.facelets.bean.Cadastro;
import br.com.facelets.bean.Login;
public class CadastroSaver {
private static final Hashtable saver = new Hashtable();
public static void salvar(Login login,Cadastro cadastro){
saver.put(login, cadastro);
}
public static Cadastro busca(Login login){
return saver.get(login);
}
}


E agora o faces-config e as páginas propriamente ditas.
Listagem 4- faces-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<faces-config version="1.2" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xi="http://www.w3.org/2001/XInclude"
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_1_2.xsd">
<managed-bean>
<managed-bean-name>cadastro</managed-bean-name>
<managed-bean-class>br.com.facelets.bean.Cadastro</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
<managed-property>
<property-name>nome</property-name>
<property-class>java.lang.String</property-class>
<value/>
</managed-property>
<managed-property>
<property-name>login</property-name>
<property-class>java.lang.String</property-class>
<value/>
</managed-property>
</managed-bean>
<navigation-rule>
<from-view-id>/pages/dados.xhtml</from-view-id>
<navigation-case>
<from-outcome>ok</from-outcome>
<to-view-id>/pages/inputname.xhtml</to-view-id>
<redirect/>
</navigation-case>
</navigation-rule>
<navigation-rule>
<from-view-id>/pages/login.xhtml</from-view-id>
<navigation-case>
<from-outcome>ok</from-outcome>
<to-view-id>/pages/greeting.xhtml</to-view-id>
<redirect/>
</navigation-case>
<navigation-case>
<from-outcome>erro</from-outcome>
<to-view-id>/pages/login.xhtml</to-view-id>
</navigation-case>
</navigation-rule>
<application>
<view-handler>com.sun.facelets.FaceletViewHandler</view-handler>
<resource-bundle>
<base-name>resources</base-name>
<var>msg</var>
</resource-bundle>
<message-bundle>resources</message-bundle>
</application>
</faces-config>





Listagem 5 – menu.xhtml

<!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:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<body bgcolor="#ffffff">
<table style="border:1px solid #CAD6E0" align="center" cellpadding="0" cellspacing="0" border="0">
<tbody>
<tr>
<td colspan="2" valign="bottom" height="1" bgcolor="#CAD6E0">
<table>
<tr>
<td>
<a href="dados.jsf">Incluir dados</a>
</td>
</tr>
<tr>
<td>
<a href="login.jsf">Login</a>
</td>
</tr>
</table>
</td>
</tr>
</tbody>
</table>
</body>
</html>



Listagem 6 – common.xhtml

<!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:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<span jsfc="f:loadBundle" basename="resources" var="msg"/>
<head>
<title>
<ui:insert name="pageTitle">JavaWora</ui:insert>
</title>
<style type="text/css">
body {
font-family: Verdana, Arial, Helvetica, sans-serif;
font-size: 14px;
}
.header {
font-family: Verdana, Arial, Helvetica, sans-serif;
font-size: 18px;
}
.bottom {
font-family: Verdana, Arial, Helvetica, sans-serif;
font-size: 9px;
text-align: center;
vertical-align: middle;
color: #8E969D;
}
</style>
</head>
<body bgcolor="#ffffff">
<table style="border:1px solid #CAD6E0" align="center" cellpadding="0" cellspacing="0" border="0" width="400">
<tbody>
<tr>
<td class="header" align="center" valign="middle" width="100%" bgcolor="#E4EBEB">
<ui:insert name="pageHeader">JavaWora Facelets</ui:insert>
</td>
</tr>
<tr>
<td height="1" width="100%" bgcolor="#CAD6E0"></td>
</tr>
<tr>
<td width="100%">
<table width="100%" style="height:150px" align="left" cellpadding="0"
cellspacing="0" border="0">
<tbody>
<tr>
<td align="center" width="10%" valign="middle">
<ui:insert name="menu">
<ui:include src="/templates/menu.xhtml"/>
</ui:insert>
</td>
<td align="center" width="75%" valign="middle">
<ui:insert name="body">Page Body</ui:insert>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
<tr>
<td colspan="2" valign="bottom" height="1" width="100%" bgcolor="#CAD6E0"></td>
</tr>
</tbody>
</table>
</body>
</html>



Listagem 7 – cadastro.xhtml

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
template="/templates/common.xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
<title>Cadastro</title>
</head>
<ui:define name="body">
<form jsfc="h:form" id="cadastro">
<table>
<tr>
<td>Nome*:</td>
<td>
<input type="text" jsfc="h:inputText" value="#{cadastro.nome}" id="nome" label="#{msg.lbl.nome}" required="true" requiredMessage="#{msg.erroRequired}"/>
</td>
<td><h:message for="nome" id="erroNome"></h:message> </td>
</tr>
<tr>
<td>login*:</td>
<td>
<input type="text" jsfc="h:inputText" value="#{cadastro.login}" id="login" label="#{msg.lbl.login}" required="true" requiredMessage="#{msg.erroRequired}"/>
</td>
<td><h:message for="login" id="erroLogin"></h:message> </td>
</tr>
<tr>
<td>senha*:</td>
<td>
<input type="text" jsfc="h:inputSecret" value="#{cadastro.senha}" id="senha" label="${msg.lbl.senha}" required="true" requiredMessage="#{msg.erroRequired}"/>
</td>
<td><h:message for="senha" id="erroSenha"></h:message> </td>
</tr>
<tr>
<td>Estado Civil:</td>
<td>
<select jsfc="h:selectOneMenu" value="#{cadastro.idEstado}" id="eCivil" label="#{msg.lbl.estadoCivil}">
<option jsfc="f:selectItem" itemValue="1" itemLabel="Casado"></option>
<option jsfc="f:selectItem" itemValue="2" itemLabel="Solteiro"></option>
<option jsfc="f:selectItem" itemValue="3" itemLabel="Divorciado"></option>
</select>
</td>
</tr>
<tr>
<td colspan="2" align="center">
<input type="submit" value="Salvar" jsfc="h:commandButton" action="#{cadastro.salvar}"/>
</td>
</tr>
</table>
</form>
</ui:define>
</ui:composition>



Listagem 8 – inputname.xhtml

<!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:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:c="http://java.sun.com/jstl/core">
<ui:composition template="/templates/common.xhtml">
<ui:define name="pageTitle">
Bem Vindo
</ui:define>
<ui:define name="pageHeader">
Home
</ui:define>
<ui:define name="body">
${msg.prompt}
</ui:define>
</ui:composition>
</html>



Listagem 9 – greeting.xhtml

<!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:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<f:loadBundle basename="resources" var="msg" />
<ui:composition template="/templates/common.xhtml">
<ui:define name="body">
#{msg.greeting} <span jsfc="h:outputLabel" value="#{sessionScope.user.nome}"/>!
</ui:define>
</ui:composition>
</html>



Listagem 10 – login.xhtml



<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
template="/templates/common.xhtml">
<ui:define name="body">
#{erro}
<form jsfc="h:form" id="login">
<table>
<tr>
<td>Login:</td><td><input jsfc="h:inputText" value="#{cadastro.login}" type="text"/></td>
</tr>
<tr>
<td>Senha:</td><td><input jsfc="h:inputSecret" value="#{cadastro.senha}" type="password"/></td>
</tr>
<tr>
<td colspan="2" align="center">
<input jsfc="h:commandButton" action="#{cadastro.logar}" type="submit" value="OK"/>
</td>
</tr>
</table>
</form>
</ui:define>
</ui:composition>



Experimente abrir a pagina de login no firefox, ie, ou qualquer ferramenta WYSIWYG (acho q é isso) e veja que os parametros estão lá, até a pagina, mas como isso é possível? Ora como eu disse trata-se de uma página simples xhml, a propósito podemos trabalhar com xhtml diretamente em facelets, ou mesmo jsps, mas xhtml é mais legal. A aplicação consiste em efetuar um cadastro e logo após um login para verificar se os dados foram armazenados.
Agora vamos as curiosidades. Conforme observado no web.xml, há dois listeners registrados, eles são necessários para o Tomcat 5.5, pois o compilador utilizado no facelets não é o Jasper que vêm nele, mas um próprio.
No web.xml observe a sentença:


<context-param>
<param-name>facelets.DEVELOPMENT</param-name>
<param-value>true</param-value>
</context-param>

Cause um erro em uma página e observe o que ocorre, figura 2:






O compilador descreve o erro e lhe auxilia com a arvores de componentes gerada e os dados do que estavam presentes. Legal né! Pois agora troque a sentença para false. Você verá que o erro agora é apresentado como na figura 3. Bonito não?!







Obrigado e até mais!

11 comentários:

Jogador de Basket disse...

Onde posso baixar os jar, necessarios para o Tomcat 6????

Jogador de Basket disse...

Estou utilizando a implementação da Apache, o myfaces.

Alexandre Campelo disse...

trabalhar com o myfaces e o facelts requer algumas alterações.
Primeiro para o caso de usar Tomcat 6 retire o jar "el-api".
Para usar com o myfaces, a lista de jars altera um pouco:
-commons-annotations
-commons-beanutils
-commons-codec
-commons-collections
-commons-digester
-commons-el
-commons-fileupload
-commons-lang
-commons-logging
-el-ri
-jsf-facelets
-jstl
-myfaces-api
-myfaces-impl
E finalmente o web.xml deve ser alterado para os parâmetros do myfaces.

Felipe Guerra disse...

Amigo,

Pq vc não inclui as tags abaixo no seu web.xml?

[context-param]
[param-name]javax.faces.CONFIG_FILES[/param-name]
[param-value]/WEB-INF/faces-config.xml[/param-value]
[/context-param]

Alexandre Campelo disse...

Por default ele busca um arquivo faces-config.xml, com esse mesmo nome, caso não encontre declarativamente no web.xml, ou seja é desnecessário quando se usa apenas um arquivo de configuração do faces.

Rafael Ponte disse...

Excelente post! Atualmente estamos com uma enorme carência de blogs nacionais sobre JavaServer Faces. Tutoriais e how-to's são um ótimo começo de difundir esta tecnologia.

Só uma coisa, o código do post meio complicado de ler, talvez se você utilizasse algum plugin para formatar códigos ficaria bem melhor de ler.

Enfim, parabéns!

Marcelo disse...

Bom dia Alexandre.

Li o tutorial e segui a receita passo-a-passo para fazer rodar a aplicação, mas estou tendo problemas quando tento executar estou recebendo tendo esta exception:

SEVERE: Missing Built-in Tag Libraries! Make sure they are included within the META-INF directory of Facelets' Jar
07:57:27,050 ERROR [STDERR] Sep 30, 2008 7:57:27 AM com.sun.facelets.FaceletViewHandler handleRenderException
SEVERE: Error Rendering View[/pages/inputname.xhtml]
java.lang.NullPointerException
at com.sun.facelets.compiler.NamespaceHandler.apply(NamespaceHandler.java:49)
at com.sun.facelets.compiler.EncodingHandler.apply(EncodingHandler.java:25)
at com.sun.facelets.impl.DefaultFacelet.apply(DefaultFacelet.java:95)
at com.sun.facelets.FaceletViewHandler.buildView(FaceletViewHandler.java:510)
at com.sun.facelets.FaceletViewHandler.renderView(FaceletViewHandler.java:553)
at org.ajax4jsf.application.ViewHandlerWrapper.renderView(ViewHandlerWrapper.java:108)
at org.ajax4jsf.application.AjaxViewHandler.renderView(AjaxViewHandler.java:189)
at com.sun.faces.lifecycle.RenderResponsePhase.execute(RenderResponsePhase.java:106)
at com.sun.faces.lifecycle.LifecycleImpl.phase(LifecycleImpl.java:251)
at com.sun.faces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:144)
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:245)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:630)
at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:436)
at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:374)
at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:302)
at org.apache.jasper.runtime.PageContextImpl.doForward(PageContextImpl.java:694)
at org.apache.jasper.runtime.PageContextImpl.forward(PageContextImpl.java:665)
at org.apache.jsp.index_jsp._jspService(index_jsp.java:59)
at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:803)
at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:369)
at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:337)
at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:266)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:803)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.jboss.web.tomcat.filters.ReplyHeaderFilter.doFilter(ReplyHeaderFilter.java:96)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:175)
at org.jboss.web.tomcat.security.SecurityAssociationValve.invoke(SecurityAssociationValve.java:189)
at org.jboss.web.tomcat.security.JaccContextValve.invoke(JaccContextValve.java:90)
at org.jboss.web.tomcat.security.SecurityContextEstablishmentValve.invoke(SecurityContextEstablishmentValve.java:96)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
at org.jboss.web.tomcat.service.jca.CachedConnectionValve.invoke(CachedConnectionValve.java:157)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:309)
at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:844)
at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:601)
at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:447)
at java.lang.Thread.run(Unknown Source)

Tem alguma ideia do que eu não fiz para receber esta Exception?

Abraços,

Marcelo.

Alexandre Campelo disse...

Veja se a lista de jars corresponde ao que eu informei.
E observe bem como está o web.xml, e se o faces-config também está devidamente configurado.

Anônimo disse...

Seria muito bom se colocasse pra download o WAR do projeto e/ou os arquivos para download.

Mário Romeu da Silva Junior disse...

Interessante....

Estou utilizando o facelets hoje em dia no meu trabalho ( 14 agosto 2009 ) e estou começando a gostar dele...

Mas tenho que admitir que sem sombra de dpúvidas.. ainda fico com o Apache Wicket ( http://wicket.apache.org/ ).

Parabéns pelo tutorial... facilitou o início no desenvolvimento com facelets.

Anônimo disse...

Good fill someone in on and this mail helped me alot in my college assignement. Thanks you as your information.