Décio Heinzelmann Luckow ([email protected]): é bacharel em Sistemas de Informação pela Univille e pósgraduando em Gestão de Projetos pela Sustentare Escola de Negócios. Já trabalhou com as linguagens ASP, PHP, Python e tem se especializado nas tecnologias Java, atuando no desenvolvimento Web e integração de sistemas. Possui as certificações Java SCJP 1.5 e SCWCD. Atua como analista de sistemas na TOTVS, na área de Inteligência Empresarial. É autor do livro Programação Java para a Web da editora Novatec, além de artigos pela revista MundoJ. Além do JSF: Matriz de Campos com Recursos e Práticas Incomuns Aprenda a construir uma tela unindo JSF e JSTL permitindo o cruzamento de informações por meio de campos em matriz. Este artigo ensinará como criar formulários com campos em matriz usando JavaServer Faces e JSTL, utilizando recursos como eventos de campos, validação e conversão. Serão abordadas duas técnicas diferentes para envio dos dados, além de recursos interessantes e práticas incomuns do JSF e JSTL. Estas técnicas são importantes para ir além da flexibilidade natural do JSF e criar telas muito mais dinâmicas. utilização de campos em matriz em um formulário permite a exibição e manutenção organizada de uma grande quantidade de informações. Esta técnica é especialmente interessante quando os dados a serem apresentados e editados representam o cruzamento de outras informações, como, por exemplo: valor por mês x categoria para uma planilha de orçamento. A característica fortemente dinâmica de um formulário em matriz construído da forma como demonstraremos permite que o montante dos dados apresentados seja alterado sem mexer na estrutura da tela construída. Para conseguir isto utilizaremos as tags básicas do JSF funcionando em conjunto com a JSTL, eventos de campo e um novo tipo de escopo de Managed Bean introduzido na versão 2 do JSF. Serão demonstradas duas técnicas diferentes para se obter os dados do formulário, sendo que a segunda técnica utilizará um recurso de uma forma pouco explorada no JSF. Este artigo mostrará recursos avançados do JavaServer Faces e soluções para problemas conhecidos por desenvolvedores experientes, porém a didática do artigo permite que o leitor com pouca experiência em JSF entenda e construa todo o exemplo proposto. 25 : : www.mundoj.com.br : : Campos em matriz com JavaServer Faces A criação de um formulário com matriz de campos não é algo inédito, este pode ser criado dinamicamente em JSP ou puramente com JSTL. Porém neste artigo vamos focar na criação deste mesmo recurso utilizando exclusivamente o JavaServer Faces e JSTL. Isto permite que possamos contar ao mesmo tempo com as facilidades e funcionalidades do JSF e com a organização e limpeza do código-fonte que ele provê. Neste artigo utilizaremos como padrão a Eclipse IDE para JavaEE, porém não existirá qualquer recurso exclusivo desta IDE, sendo que você poderá utilizar uma diferente se preferir. No Eclipse crie um novo Dynamic Web Project chamado matrizjsf (consulte o quadro “Configurando o Apache Tomcat no Eclipse” se necessário). Copie para a pasta WEB-INF\lib do projeto as bibliotecas do JSF 2 e JSTL 1.2. O JSF2 é composto pelos arquivos jsf-api.jar e jsf-ri.jar que podem ser obtidos no endereço https:// javaserverfaces.dev.java.net/. Já o JSTL é composto pelos arquivos jstl-api-1.2.jar e jstl-impl-1.2.jar e pode ser obtido no endereço https://jstl.dev.java.net/download.html. Configurando o Apache Tomcat no Eclipse Para que se possa executar um Dynamic Web Project no Eclipse é necessário ter um servidor web configurado. Para isso vamos configurar o Apache Tomcat dentro do Eclipse. Para obter o servidor web Apache Tomcat, acesse o site http:// tomcat.apache.org e faça o download o arquivo ZIP da versão 6.0, extraindo-o na sua unidade c:. No Eclipse acesse Window > Preferences > Server > Runtime Environments. Clique em Add e selecione Apache Tomcat 6, clique em Next; selecione o local de instalação do Apache Tomcat e clique em Finish. Clique com o botão direito do mouse no projeto criado (matrizjsf) e selecione Run As > Run on Server. Selecione novamente Apache Tomcat 6, clique em Next e depois em Finish. Neste momento o servidor Apache Tomcat já irá iniciar por dentro do Eclipse. Crie também uma página padrão do projeto com o nome index.jsp na pasta WebContent. Esta poderá ter o link para a página JSF de orçamento que criaremos em seguida, conforme o exemplo: <a href="orcamento.jsf">Exemplo Orçamento</a> Para manipular o servidor posteriormente acesse a view Servers (Window > Show View > Server), clique com o botão direito do mouse no servidor em questão e acione Start, Stop ou Restart. Listagem 1. Configuração do aplicativo no arquivo web.xml. <?xml version=”1.0” encoding=”UTF-8”?> <web-app ...> <display-name>MundoJ</display-name> <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>*.jsf</url-pattern> </servlet-mapping> <context-param> <param-name>javax.faces.DEFAULT_SUFFIX</param-name> <param-value>.xhtml</param-value> </context-param> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> </web-app> Na classe OrcamentoBean teremos dois java.util.List categoria e mês que serão populados no construtor com as informações que queremos cruzar. Veja que estes dois List estão sendo populados com a String do nome da categoria e do mês. Para saber como guardar mais informações para cada item neste List, consulte o quadro “Incrementando o cruzamento de informações”. Além disso, criamos um Map<String,Map<String,Float>> que preencherá e receberá as informações do formulário, este está declarado como um Map dentro do outro, o que garante que tenhamos duas chaves String para cada valor. Cada campo que for gerado no formulário JSF referenciará uma combinação de categoria e mês. Por isso que é obrigatório que cada posição desta seja criada no Map previamente. Desta forma, a inicialização do Map com todas as combinações possíveis é obrigatória. Esta inicialização deve ser chamada dentro do método getValores() da classe OrcamentoBean, quando a referência valores estiver nula, conforme a Listagem 2. Veja que esta classe tem as anotações comuns do JSF para um managed bean, que são @ManagedBean e @RequestScoped indicando que será criada uma nova instância desta classe para cada requisição realizada pelo usuário. Listagem 2. Código-fonte da classe OrcamentoBean. A configuração necessária será no arquivo \WEB-INF\web.xml e será apenas a configuração básica do JSF, conforme a Listagem 1. Para exemplificar este artigo construiremos uma página de orçamento, onde será possível informar um valor para cada combinação de categoria e mês. Desta forma, o usuário poderá informar R$ 250 para Alimentação em janeiro, R$ 500 para Educação em julho e assim por diante. Como a página é totalmente dinâmica você poderá exibir quantos meses e quantas categorias quiser, ou ainda utilizar esta mesma técnica para o cruzamento de outros tipos de informação. 26 package br.com.mundoj.web; import java.util.*; import javax.faces.bean.*; @ManagedBean @RequestScoped public class OrcamentoBean { private List<String> categorias = new ArrayList<String>(); private List<String> meses = new ArrayList<String>(); private Map<String,Map<String,Float>> valores; Cont. Listagem 2. Código-fonte da classe OrcamentoBean. public OrcamentoBean() { this.categorias = Arrays.asList(new String[]{“Alimentação”,”Educação” ,”Transportes”,”Lazer”,”Saude”,”Impostos”}); this.meses = Arrays.asList(new String[]{“Jan”,”Fev”,”Mar”,”Abr”,”Mai ”,”Jun”,”Jul”,”Ago”,”Set”,”Out”,”Nov”,”Dez”}); } private void inicializaValores() { this.valores = new HashMap<String,Map<String,Float>>(); Map<String, Float> valorMes = null; for (String cat:this.categorias) { valorMes = new HashMap<String, Float>(); for (String mes:this.meses) { valorMes.put(mes, new Float(0)); } this.valores.put(cat, valorMes); } } public void salvar() { Map<String, Float> valorMes = null; for (String cat:this.categorias) { valorMes = this.valores.get(cat); for (String mes:this.meses) { System.out.println(cat+” x “+mes+” = “+valorMes.get(mes)); } } A página de orçamento será construída usando xhtml, que é o formato padrão para o JSF 2. Esta página utilizará tags do JSF e do JSTL, o que apresentará um case bastante interessante de união destas duas tecnologias. Muito se comenta atualmente sobre o uso dos componentes do JSF e das tags do JSTL juntos, sabe-se que o uso do JSTL está bastante limitado em conjunto com o JSF e que em diversos casos recomenda-se substituir a tag <c:forEach> pela <ui:repeat> e a <c:if> pelo uso do atributo rendered dos componentes JSF, por exemplo. Porém, esta limitação se dá pelo fato das tags do JSTL e dos componentes do JSF terem ciclos de vida diferentes, sendo que estes são executados em momentos diferentes durante o processamento de uma página JSF. Por isso é preciso entender bem como atua o JSTL e JSF no processamento de uma página para saber quando usá-los. Veja o quadro “Compatibilidade entre JSF e JSTL” para se aprofundar nestas diferenças de ciclos de vida. Compatibilidade entre JSF e JSTL O JSTL nasceu há muito tempo para facilitar o desenvolvimento de páginas JSP, ele cria tags amigáveis para serem utilizadas no lugar de scriptlets contendo lógica de programação, os famosos <%%>. O problema é que o ciclo de vida de uma página JSP é totalmente diferente do ciclo de vida de uma página JSF. O JSP é focado em gerar HTML para ser diretamente entregue ao navegador, já o JSF é focado em criar uma árvore de componentes, que posteriormente serão renderizados em formato HTML. Porém a etapa de gerar o HTML de uma página JSF (fase Render Response) é apenas a última fase do seu ciclo de vida, antes disso ainda temos as fases: Restore view Apply request values } Process validations public Map<String, Map<String, Float>> getValores() { if (this.valores == null) { this.inicializaValores(); } return this.valores; } Update model values public void setValores(Map<String, Map<String, Float>> valor) { this.valores = valor;} public List<String> getCategorias() {return categorias;} public void setCategorias(List<String> categorias) { this.categorias = categorias;} public List<String> getMeses() {return meses;} public void setMeses(List<String> meses) {this.meses = meses;} } Ainda na classe da Listagem 2, o método salvar() será referenciado pelo botão Salvar da tela. Quando este método for chamado, o JSF já terá preenchido o Map valores com todos os dados que estavam em tela. A partir deste ponto, você poderá navegar por este Map da mesma forma que fez a navegação na inicialização para obter o valor para cada combinação de categoria e mês, conforme já está codificado no método salvar(). A partir deste ponto você pode fazer a persistência destas informações da forma que quiser, não sendo este o objetivo do artigo. Invoke application Render response Todas estas fases trabalham em cima de uma árvore de componentes que foi definida na página JSF (que hoje pode ser um JSP ou XHTML). Quando o JSP é usado juntamente com JSF ele também passa a ter como único objetivo a montagem da árvore de componentes do JSF (para o XHTML isto é natural) e não a geração direta do HTML como é originalmente. Por isso é que atualmente a utilização do JSTL junto ao JSF é limitado a aquelas tags que não têm como objetivo direto a geração do HTML, mas sim de manipular a árvore de componentes da página. Desta forma, tags como <c:out>, <c:import>, <c:redirect> e <c:url> não estão mais disponíveis para as páginas JSF sejam elas usando JSP ou XHTML. Hoje, apesar do JSF funcionar com o JSP, o formato de página oficial é o XHTML, o JSP se mantém como opção por questões políticas e de legado. Na página que construiremos vamos utilizar a tag <c:forEach> em uma situação bastante interessante e que deixará bem clara a sua utilidade neste caso e como ela funciona. Veja na Listagem 3 como a página ficou construída. 27 : : www.mundoj.com.br : : Listagem 3. Código-fonte da página orcamento.xhtml. <?xml version=”1.0” encoding=”ISO-8859-1”?> <!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:c=”http://java.sun.com/jsp/jstl/core” xmlns:h=”http://java.sun.com/jsf/html” xmlns:f=”http://java.sun.com/jsf/core”> <h:head> <title>Orçamento</title> </h:head> <h:body> <h1>Orçamento</h1> <h:form id=”orcamento”> <h:messages /> <h:dataTable value=”#{orcamentoBean.categorias}” var=”cat”> <h:column> <f:facet name=”header”>Categoria</f:facet> <h:outputText value=”#{cat}” /> </h:column> <c:forEach var=”mes” items=”#{orcamentoBean.meses}”> <h:column> <f:facet name=”header”>#{mes}</f:facet> <h:inputText value=”#{orcamentoBean.valores[cat][mes]}” size=”2” style=”text-align: right;”/> </h:column> </c:forEach> </h:dataTable> <h:commandButton action=”#{orcamentoBean.salvar}” value=”Salvar” /> </h:form> </h:body> </html> Podemos perceber que a <h:dataTable> irá trabalhar com as informações das categorias, ou seja, a dataTable de orçamento terá os nomes das categorias como linhas. A primeira coluna desta tabela terá como rótulo o texto Categoria e como valor o nome de cada categoria declarada na classe OrcamentoBean. As colunas seguintes desta dataTable serão formadas por cada mês que ficou declarado na classe. Perceba que a segunda <h:column> está envolvida em uma tag <c:forEach>, ou seja, estaremos gerando uma <h:column> para cada mês. Neste caso, o <c:forEach> está atuando como um gerador de código-fonte JSF. Ele consegue isto pelo fato do JSTL ser executado antes do JSF no processamento da página. É justamente por isso que o uso do JSTL está limitado junto com o JSF. Devido a este comportamento, você não pode utilizar tags JSTL que dependam de valores gerados por tags JSF, pois na verdade o JSTL executa antes do JSF. Porém, ao mesmo tempo, os managed beans do JSF estão disponíveis para JSTL, por isto que conseguimos fazer o <c:forEach> navegar pelos meses do orcamentoBean. Perceba também que neste caso o uso da tag <ui:repeat> do Facelets para gerar as colunas da dataTable não seria possível, pois a tag <ui:repeat> não será aceita como uma sub-tag da <h:dataTable>. O caso do <c:forEach> funciona corretamente, pois na verdade o 28 JSF nem sabe da existência dele, já que o JSF só irá trabalhar com o conteúdo que já foi previamente gerado pelo JSTL, ou seja, uma porção de <h:columns> para os meses declarados. Agora analisando melhor o uso do <c:forEach> e do <h:column> no exemplo da Listagem 3 podemos perceber que a quantidade de loops do <c:forEach> será dada pela quantidade de meses declarados. Veja que o <c:forEach> está sendo alimentado com a informação meses do managed bean orcamentoBean. Para definirmos o rótulo da coluna de cada mês e a indexação do Map, utilizamos o var=”mes” definido no <c:forEach> que contém o mês do loop atual. O campo que exibirá e receberá os valores na tela será definido pelo componente do JSF <h:inputText>. O atributo value deste componente apontará para a expressão #{orcamentoBean. valores[cat][mes]}. Veja que mesmo sendo um Map e não um array, a expressão UEL utilizada funciona como se fosse um array e que, neste caso, está sendo indexado por valores String. Se a propriedade valores fosse um Float[][], por exemplo, esta expressão seria a mesma, só que teria que ser indexado por valores numéricos. Para testar o exemplo proposto, basta fazer o Start do servidor e abrir a url http://localhost:8080/matrizjsf/orcamento.jsf, conforme a figura 1. Figura 1. Tela JSF com campos em matriz. Incrementando o cruzamento de informações No exemplo, os java.util.List de categorias e meses são alimentados diretamente com Strings dos nomes das categorias e dos meses. Porém, conforme a necessidade, você poderia utilizar também objetos POJO do Java, contendo, por exemplo, número e nome do mês, e código e descrição da categoria. Ao invés de fazer um categorias.add(“Alimentação”), faria um categorias.add(new Categoria(1,”Alimentação”)), sendo o mesmo para os meses. Para o caso da utilização da classe POJO, lembre-se que é necessária a implementação dos métodos equals() e hashCode() corretamente. O Eclipse possui recurso para gerar automaticamente estes dois métodos, para isso acesse o menu de contexto da classe, opção Source > Generate hashcode() and equals(), depois que já tiver definido as propriedades da classe. Neste caso, ao exibir o nome do mês e descrição da categoria, utilizaria as seguintes expressões respectivamente: #{mes. nome} e #{cat.descricao}. Assim como a indexação do Map já utilizado, ou de um Float[][] para os valores, poderia ser feito usando números com indexadores, exemplo: #{orcamentoBean.valores[cat.codigo][mes.numero]}. Reconhecendo os valores alterados O exemplo construído até agora exibe e envia todos os valores do formulário, que podem ser manipulados no método salvar(). Mas se quiséssemos receber apenas os valores que foram alterados no formulário e não todos, como fazer? Neste caso entra um recurso bastante interessante do JSF que é o valueChangeListener. Este permite que um método específico seja chamado somente se o valor do campo em questão for alterado. Diferente do que a maioria das pessoas pensa, este método somente será chamado quando o formulário for submetido, e não quando o usuário sair do campo em tela. Além disso, ele será chamado antes do método que disparou a ação, neste caso o método referenciado no atributo valueChangeListener será chamado antes do método salvar(). Para utilizar o valueChangeListener, o primeiro passo é incluir este atributo no campo em questão, apontando para o método que deverá se chamado, conforme a Listagem 4. Listagem 4. Fragmento de código para utilizar o valueChangeListener. <h:inputText value=”#{orcamentoBean.valores[cat][mes]}” size=”2” style=”text-align: right;” valueChangeListener=”#{orcamentoBean.mudouValor}”/> O método para o qual apontará o atributo valueChangeListener deverá ter a seguinte assinatura: void meuMetodo(javax.faces. event.ValueChangeEvent). Neste caso, este método receberá um objeto ValueChangeEvent contendo as informações do evento ocorrido. Dentre as informações mais interessantes podemos destacar os métodos event.getOldValue() e event.getNewValue(), fornecendo o antigo e o novo valor do campo, respectivamente. O valueChangeListener funcionará em qualquer condição, porém a informação do getOldValue() somente estará disponível se o escopo do managed bean da tela for maior que o request, ou seja, funcionará com os escopos @ViewScoped, @SessionScoped ou @ ApplicationScoped. Isto porque, diferente do que muitos pensam, o “old value” não é que o valor do campo antes da tela ser exibida, mas o valor do campo antes de receber o novo valor, quando o formulário for submetido. Por isso que o managed bean precisa ter um escopo maior que uma requisição, ou seja, que a instância do managed bean não deixe de existir ao final de uma requisição. Caso o managed bean não seja de uns dos escopos sugeridos, o evento value change sempre será disparado, pois neste caso o old value será sempre nulo e o new value será o valor da tela, ou seja, valores diferentes. Por isso no caso da classe OrcamentoBean, seu escopo deve ser alterado para @ViewScoped, que foi introduzido na última versão do JSF. Ele tem este nome justamente porque a instância do managed bean fica armazenada na tela (na view). Desta forma, a instância do managed bean se manterá a mesma enquanto a mesma tela estiver sendo exibida, o usuário pode recarregar, submeter e reexibir a tela, que a instância continuará existindo. Outra questão importante, mas que está restrita ao fato de trabalharmos com informações em matriz é quanto ao valor do campo que receberemos no evento value change. O fato de sabermos qual o “new value” do campo alterado não nos diz de qual categoria e de qual mês este valor veio. Como podemos resolver esta questão? Neste momento entra como sugestão um uso incomum para uma tag do JSF, que é a tag <f:attribute>. Desta forma, criaremos nossos próprios atributos para os campos da matriz, e recuperando-os na ocorrência do evento. Veja na Listagem 5 como ficará a definição do campo utilizando este recurso. Listagem 5. Uso customizado da tag <f:attribute>. <h:inputText value=”#{orcamentoBean.valores[cat][mes]}” size=”2” style=”text-align: right;” valueChangeListener=”#{orcamentoBean.mudouValor}”> <f:attribute name=”cat” value=”#{cat}”/> <f:attribute name=”mes” value=”#{mes}”/> </h:inputText> Mesmo que os atributos cat e mes não sejam oficiais do JSF, ele aceita que estes sejam definidos. Veja na Listagem 6 como ficará definido o método mudouValor() na classe OrcamentoBean, e a recuperação dos atributos customizados. Listagem 6. Método mudouValor da classe OrcamentoBean. public void mudouValor(ValueChangeEvent event) { String cat = (String) event.getComponent().getAttributes().get(“cat”); String mes = (String) event.getComponent().getAttributes().get(“mes”); Float _old = (Float) event.getOldValue(); Float _new = (Float) event.getNewValue(); System.out.println(cat+” x “+mes+” de: “+ _old + “ para: “ + _new); } Validação Listagem 7. Identificação do campo para validação. <h:inputText value=”#{orcamentoBean.valores[cat][mes]}” size=”2” style=”text-align: right;” label=”Valor para #{cat} em #{mes}” required=”true” valueChangeListener=”#{orcamentoBean.mudouValor}”> <f:attribute name=”cat” value=”#{cat}”/> <f:attribute name=”mes” value=”#{mes}”/> </h:inputText> Uma questão importante, caso seja necessário algum tipo de validação para os campos em matriz é como identificar o campo que não passou na validação. O componente <h:inputText>, assim como os outros campos de formulário, possuem o atributo label. Este atributo é a identificação do campo para as mensagens de validação. Para o caso de matriz de campos é interessante identificar no label o cruzamento das informações. Na Listagem 7 exibimos a configuração deste campo para que seja facilmente identificável em uma mensagem de validação. Utilizando uma validação básica como o required=”true” e o atributo label, ao submeter o formulário com algum campo em branco a seguinte mensagem será apresentada. Valor para Alimentação em Fev: Erro de validação: o valor é necessário. 29 : : www.mundoj.com.br : : Formatação e conversão Listagem 8. Conversor customizado para campo Float em matriz. package br.com.mundoj.web; import java.text.NumberFormat; import java.text.ParseException; import javax.faces.component.UIComponent; import javax.faces.context.FacesContext; import javax.faces.convert.Converter; import javax.faces.convert.ConverterException; import javax.faces.convert.FacesConverter; @FacesConverter(value = “mundoj.DecimalConverter”) public class DecimalConverter implements Converter { private NumberFormat nf; public DecimalConverter() { this.nf = NumberFormat.getNumberInstance(); this.nf.setMinimumFractionDigits(2); this.nf.setMaximumFractionDigits(2); } @Override public Object getAsObject(FacesContext context, UIComponent component, String value) { try { return new Float(nf.parse(value).floatValue()); } catch (ParseException e) { throw new ConverterException(e.getMessage()); } } @Override public String getAsString(FacesContext context, UIComponent component, Object value) { return nf.format(value); } } Como você deve ter percebido, os valores que estão sendo apresentados no formulário estão no formato padrão do Java 0.0 e não 0,00 como seriam de se esperar para informação monetária no Brasil. Para uma questão como essa a primeira solução que vem a mente é utilizar a tag <f:numberFormat> que tem justamente como objetivo a formatação nesta situação. Porém, talvez por um comportamento padrão do JSF ou a forma altamente dinâmica na qual este exemplo foi construído, somente neste caso o JSF não respeita o tipo Float de destino quando utilizamos o <f:numberFormat>. Como consequência, se for digitado um valor “25” ele entenderá como um Long, se for digitado um valor 25,5 ele entenderá como um Float. Da mesma forma, os campos que forem mantidos como 0,00 terão o seu old value como Float e o seu new value como Long. Isto faz com que o JSF ache que todos os campos do formulário sofreram alteração, pois o tipo do old value está diferente do new value, mesmo que o valor nominal seja o mesmo. Assim, para todos os campos o método mudouValor() será acionado, mesmo que o valor não tenha sido alterado. Neste caso, qual seria a forma correta de se formatar o valor do campo sem que 30 estes problemas ocorram? A solução é criar um conversor customizado que force a conversão para o tipo Float, o que no JSF pode ser feito em algumas linhas de código, conforme a Listagem 8. Nesta classe, a annotation @FacesConverter se encarrega de nomear este conversor como mundoj.DecimalConverter. Este nome deve ser utilizado ao configurar o <h:inputText> da tela, informando o atributo converter=”mundoj.DecimalConverter”. Livro Programação Java para a Web Este artigo apresentou não apenas a criação de um formulário em matriz usando o JSF, mas também diversos outros recursos que podem ser aplicados de diversas formas aos seus projetos. A criação de uma página de orçamento para este objetivo segue a ideia de ensinar a tecnologia por meio de exemplos úteis e interessantes. Esta abordagem de utilizar exemplos interessantes para ensinar Java também é adotada pelo livro Programação Java para a Web da editora Novatec (www.javaparaweb.com.br). Este livro possui uma temática inovadora, com enfoque extremamente prático, que mostra passo a passo como desenvolver uma aplicação web utilizando a linguagem Java e as tecnologias mais poderosas e populares no arsenal dos desenvolvedores, como JavaServer Faces e Hibernate.O método utilizado no livro se baseia no projeto de uma aplicação financeira pessoal, em que são abordadas várias técnicas de desenvolvimento em cada etapa do projeto, desde as mais tradicionais e conhecidas até as mais modernas e avançadas. O artigo apresenta recursos que podem complementar seus próprios projetos e o projeto abordado no livro, tornando-os ainda mais interessantes. Além disso, o livro também aborda os requisitos básicos de um sistema construído de forma profissional em Java, como: JavaServer Faces, Hibernate, Facelets, CSS, Spring Security, Ajax, PrimeFaces, internacionalização, iReport e Jasper Reports, JfreeChart, WebServices e busca de informações em meios externos. Considerações finais Este artigo mostrou de forma prática a construção de uma tela em JavaServer Faces que exibe e edita informações em matriz. Utilizou recursos como união do JSF e JSTL, @RequestScoped e @ViewScoped, ValueChangeListener, validação e conversão/formatação de valores. Porém, mais do que isso, mostrou diversas técnicas interessantes e incomuns no desenvolvimento JSF e que poderão ser reaproveitadas pelo leitor em contextos diferentes do apresentado• Referências • Livro Programação Java para a Web; Luckow, Décio Heinzelmann; Melo, Alexandre Altair de. Editora Novatec. 2010. • Java EE Tutorial. http://download.oracle.com/javaee/5/tutorial/doc/. • Site do JSF: https://javaserverfaces.dev.java.net • Site do JSTL: https://jstl.dev.java.net • Site do Apache Tomcat: http://tomcat.apache.org • Site do Eclipse: http://www.eclipse.org/