Archive

Posts Tagged ‘JavaEE’

view scope no JSF 2.0

October 24th, 2008 20 comments

Eu uso JSF há um bom tempo e sempre fui um defensor de que JSF é muito bom, mas hoje em dia ainda não da pra usar ele sozinho, por isso mesmo uso JSF + Facelets + Seam. Acredito que com o que temos hoje usar JSF sozinho não é lá das coisas mais produtivas do mundo, e isso em alguns casos faz com que pessoas critiquem o JSF porque não querem adicionar mais dependências ao projeto e deixam Facelets e Seam de fora.

Pessoalmente eu acho isso quase o mesmo nível de querer fazer um projeto sem Hibernate pra não ter mais dependências no projeto. Tá certo que hoje o caso é diferente pois temos a JPA, mas se não a tivéssemos ainda, você faria um projeto de verdade só com JDBC só pra não ter essa dependência com Hibernate? Pois bem, muita gente faz isso com JSF. Mas assim como a JPA veio para deixar as pessoas mais tranquilas e menos apavoradas de ter um framework a mais na aplicação, o JSF 2.0 também vai dar uma forcinha nesse sentido. Com ele poderemos ter uma aplicação funcionando super bem sem adicionar outros frameworks para isso. E isso devido duas coisas: a inclusão do Facelets (já apresentada aqui), e o novo escopo view.

Erros de validação

O JSF faz um papel muito bem feito abstraindo a camada web. Conseguimos desenvolver uma aplicação com JSF sem colocar a mão em HttpServletRequest e outros Http* da vida. O serviço seria muito bem feito de ponta a ponta se não fosse por um detalhe: a falta de um escopo que abstraísse também o http como o restante faz. Até o JSF 1.2 parece que essa parte foi esquecida, e enquanto o Faces tratava os escopos já conhecidos de toda aplicação web, como request, session e application, deixava uma porta aberta para o surgimento de componentes como t:saveState do Tomahawk, e depois as famosas conversations, no Seam.

Mas para que conversation e saveState servem? Um dos maiores problemas do JSF é a quantidade de “erro de validação” que acabam “estourando” na cara do desenvolvedor. Claro que sabendo como o JSF trabalha nós conseguimos desenvolver sem problemas. Eu sempre comento que a regra número 1 é sempre sobrescrever o método equals. Seguir boas práticas nunca é demais. E com isso boa parte dos erros de validação simplesmente somem. Um dia eu paro pra explicar melhor isso. Mas existem erros que acontecem como os famosos combos em cascata e commandButtons/commandLinks que não executam poque estão dentro de dataTable’s que só são preenchidas depois de alguma ação.

Imagine que temos uma página de cadastro de endereço, onde existem dois combos, o primeiro para estado e o segundo para cidades. Mas inicialmente o combo de cidades vem vazio, somente depois que selecionamos o primeiro, uma ação é executada para buscar as cidades. Selecionamos então “MS”, acontece uma nova requisição, as cidades são mostradas, selecionamos “Campo Grande” e mandamos salvar. Nisso vem o famoso erro de validação no combo das cidades. Isso acontece porque pela natureza stateless do managed bean de escopo request que estamos usando, o JSF esquece que já foi feita a requisição que busca as cidades, e para ele quando clicamos em salvar é como se fosse a primeira requisição. Então o JSF pensa: “como esse cara tá submetendo uma cidade sendo que eu nem mostrei os valores desse combo ainda? Essa cara tá me sacaneando, vou tesourar essa requisição dele”. Mas como fugir disso então? Coloca tudo no escopo de sessão? A resposta para esse problema é a utilização de escopos mais refinados, não disponíveis (ainda) no JSF padrão.

Contextos mais “espertos”

Na minha opinião, uma das melhores coisas do Seam é a conversação. Um conversação é um “escopo” maior que um request e menor que a sessão do usuário, e que tem um início e fim definidos por nós. Quando iniciamos uma conversação o contexto “vira” statefull até que seja finalizada, quando então “vira” stateless novamente. Claro que o Seam é muito mais que isso, mas para o assunto do post isso é o mais relevante. Fora Conversation, o Seam tem um escopo Page, cujo funcionamento é basicamente o mesmo do saveState. Dessa forma, o contexto é statefull enquanto submetemos para a mesma página. Uma conversação é algo mais refinado, que pode envolver várias telas, mas para a grande maioria dos casos essas várias requisições que precisamos manter o estado são feitas para mesma tela, como nos exemplos dos combos de estado e cidade e da dataTable resultando de filtro com uma action dentro.

View Scope

Exatamente nesse caso que o escopo view vem suprir essa carência do JSF padrão. Um managed bean com esse escopo permanece na memória enquanto submetemos para a mesma tela. Não quero dizer que você nunca mais vai precisar de outras coisas, ou que o Seam nada mais é do que fazer de outra forma o que um saveState já fazia antes, mas sim que agora para a maioria das aplicações já poderemos usar apenas JSF 2 sem ter que usar outras ferramentas para nos auxiliar. Claro que um conjunto de componentes ricos sempre será bem vindo, e que certamente iremos continuar querendo usar algumas coisas mais específicas que o Seam/WebBeans nos oferecem. Mas o grande ganho é que para o básico não precisamos de mais nada, coisa que infelizmente hoje, com JSF 1.2, ainda não acontece.

Como já escrevi bastante, vamos a um exemplo. O nosso managed bean de escopo view ficaria assim:

@ManagedBean(name="enderecoBean")
@ViewScoped
public class EnderecoBean {
 
    @PostContruct
    public void construct() {
        //chamado só quando o managed bean é colocado no escopo view,
        //e não a cada requisição como acontecia com o escopo request
    }
 
    @PreDestroy
    public void destroy() {
        //chamado quando outra view for chamada através do UIViewRoot.setViewId(String viewId)
    }
 
}

Com esse escopo o JSF cria o managed bean na primeira requisição onde ele for preciso (se for lazy), e só o remove da memória quando for chamado o método UIViewRoot.setViewId(String viewId) para indicar que iremos trabalhar com outra página.

Categories: JavaEE, JSF Tags: , ,

JavaServer Faces 2.0 – Early Draft Review 2

September 24th, 2008 4 comments

Saiu o segundo rascunho da especificação do JSF 2.0, no entanto ainda não há uma versão da implementação da EDR2 disponível para download como já saiu para a EDR1.

Primeiramente, vou me basear nas diferenças entre as reviews 1 e 2, mas isso não significa que o que vou falar aqui é inédito pois podemos ver na net diversos post comentando o que vem por aí. E também ainda não li tudo, a idéia é compartilhar o que mais me chamou a atenção nesse primeiro contato.

FacesContext

Sem dúvida essa é uma das classes que mais manipulamos no JSF, e nela temos algumas funcionalidades novas interessantes:

  • getCurrentPhaseId() – Disponível desde a EDR1, devolve um PhaseId. Pode ser bem útil para fazermos algumas coisas só depois de uma dedeterminada fase.
  • getExecutePhaseClientIds() - Devolve uma List<String>. Guarda uma lista com os client ids dos componentes que serão processados na requisição atual. Isso porque o JSF2 tem nativamente o suporte à ajax, e submissão parcial da página.
  • getPartialResponseWriter() - Devolve o ResponseWriter para os componentes de uma renderização parcial.
  • getRenderPhaseClientIds() - Devolve uma List<String> contendo os client ids dos componentes que serão renderizados em uma renderização parcial.
  • isAjaxRequest() - devolve um boolean dizendo se a requisição é ajax (auto explicativa né :) )
  • isExecuteNone() - retorna true se for uma submissão parcial mas nenhum componente precisará ser processado. Seria como dar apenas um reRender usando o ajax4jsf mas sem mandar executar nada.
  • isPostback() - método "atalho" para ResponseStateManager.isPostback(FacesContext).
  • isRenderAll() - Retorna true se for uma requisição ajax, isRenderNone() retornar falso, e getRenderPhaseClientIds() retornar uma lista vazia.
  • isRenderNone() - Retorna true caso for para executar uma renderização parcial, mas a lista de componentes a renderizar for vazia. Imagine uma requisição ajax que só envia dados ao servidor.

Para os métodos get dessa lista, também tem os respectivos set.

Annotations

Uma coisa que todo mundo esperava está disponível no EDR2, que é a possibilidade de anotar nossos managed beans com @ManagedBean, @FacesValidator, @FacesConverter e @FacesComponent entre outros.

Para quem já está habituado com o Seam vai se sentir bem a vontade, pois as anotações seguem o mesmo estilo.

Apesar de no começo parecer estranho esse prefixo "Faces" em todas essas anotações, fica útil para não confundir com as interfaces com o mesmo nome. Sem isso (no Seam é assim), como importamos a anotação e a interface com o mesmo nome, uma das duas tem que ficar com o nome totalmente especificado. Não que seja problema, mas com esse prefixo fica mais "limpinho".

@ManagedBean

  • name - nome do managed bean
  • scope- escopo. O que não pareceu tão legal é que a gente passa uma String ("request", "session" ou "application"), quando seria mais bacana uma Enum como o Seam faz. O valor default é "none".
  • eager - se for true, o managed bean será startado junto com a aplicação, e o "escope" passado será ignorado, e o managed bean será do escopo application. Se for false, fica como é hoje (lazy). O default é false.

@RequestScoped, @SessionScoped, @ApplicationScoped, @NoneScoped, @ViewScoped, @CustomScoped

  • Cada uma das anotações representando seus respectivos escopos

@FacesConverter

  • value - string que representa o converter-id do conversor
  • forClass - passamos o java.lang.Class da classe que queremos registrar o conversor na forma de converter-for-class

@FacesValidator

  • value - string que representa o validator-id do validador

@FacesComponent

  • value - string que representa o component-type do UIComponente

Facelets 2

O outro assunto que de cara me interessou foi a integração do JSF com o Facelets, pois já uso Facelets há um bom tempo e nem me imagino fazendo uma aplicação em JSF sem ele. Tanto que até escrevi uma matéria pra a MundoJava sobre Facelets e as novidades do JSF 1.2. Na época eu comentei sobre a versão 1.2 do Facelets que nunca chegou a sair, talvez porque o pessoal passou a investir no JSFTemplating ou quem sabe viram que compensaria partir logo para um 2.0. Mas no Facelets 1.2 já podíamos ver o que provavelmente foi a base da API de Ajax para o JSF2.

No JSF2 temos o chamado PDL (Page Declaration Language), que é uma abstração para os mecanismos de definição de páginas disponíveis para o JSF, que até agora são JSP e Facelets. Porém se a gente der uma espiadinha no projeto JSFTemplating, podemos ver que existe a possibilidade de usarmos outras coisas, como Groovy por exemplo. Então é bem possível que vejamos coisas parecidas para o JSF2. Só para concluir a idéia, já é possivel usar Groovy em vez de xhtml para construir telas com Facelets, usando Gracelets. É bem bacana e eu já fiz uns testes que depois vou postar aqui também. Mas vamos voltar ao assunto.

Na EDR2 é explicado como será mantida a compatibilidade retroativa com as aplicações que usam Facelets. Basicamente será procurando dentro das classes da nossa aplicação ou das dependencias dela se existe alguma dependencia de classes do pacote com.sun.facelets e/ou dos seus subpacotes. Se houver, o Facelets embarcado no JSF não vai rodar, e as coisas vão continuar como estão, onde quem roda é o facelets que está no jar da nossa aplicação. Agora se não houver dependencia com as classes do Facelets atual, o Facelets2 entra em ação.

Composition Component com JSF/Facelets 2

Primeiramente, seria interessante dar uma olhada no suporte a recursos do JSF2 para entendermos melhor como tudo vai funcionar. Vou seguir o exemplo da documentação para facilitar.

Um composition component vai ser definifo usando o suporte a resources do JSF2. Imagine que o source do nosso componente é o foo.xhtml que está dentro da pasta ezcomp que por sua vez fica dentro da pasta de resources do JSF. Para usarmos esse componente nao precisamos mais de um arquivo taglib.xml, bastara chamarmos assim:

1
2
3
4
5
6
7
8
9
10
11
12
<!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"
 xmlns:ez="http://java.sun.com/jsf/composite/ezcomp">
 
   ...
   <ez:foo />
   ...
</html>

Como podemos ver nossos componentes ficam automaticamente visíveis usando o padrão http://java.sun.com/jsf/composite/<composite-library-name> onde <composite-library-name> é o nome da nossa pasta dentro do resources do JSF. E cada xhtml dentro dessa pasta pode ser acessado como um componente. Vimos aqui um bom exemplo de CoC no JSF2. Agora se quisermos usar um padrão diferente de nomenclatura para nossos componentes, basta usar o bom e velho arquivo de configuração de taglibs do facelets.

Agora ainda seguindo o exemplo disponível na versão snapshot (e provavelmente será a mesma do EDR2), vamos ver como fica o código de um componente definido em um arquivo chamado loginPanel.xhtml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
<!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"
      xmlns:composite="http://java.sun.com/jsf/composite">
<head>
<title>Not present in rendered output</title>
</head>
<body>
 
<composite:interface name="loginPanel"
                     displayName="Very Simple Login Panel"
                     preferred="true"
                     expert="false"
                     shortDescription="An illustration of the composite component feature">
 
  <composite:attribute name="model" required="true">
 
    <composite:attribute name="loginAction" required="true">
      <composite:deferred-method>
        <composite:method-signature>
                    java.lang.Object action()
        </composite:method-signature>
      </composite:deferred-method>
     </composite:attribute>
 
   </composite:attribute>
 
 
  <composite:editableValueHolder name="username" />
  <composite:actionSource name="loginEvent" />
  <composite:actionSource name="cancelEvent" />
  <composite:actionSource name="allEvents" targets="loginEvent,cancelEvent" />
 
  <composite:facet name="header" />
 
</composite:interface>
 
<composite:implementation>
 
<table border="1">
  <thead>
    <tr>
      <th>
     <composite:insertFacet name="header" />
      </th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>
      <p>
         <h:inputText id="username" />
      </p>
      <p>
	<h:commandButton id="loginEvent" value="Login" action="#{compositeComponent.attributes.model.loginAction}">
 
	</h:commandButton>
	<h:commandButton id="cancelEvent" value="Cancel" action="cancel">
	</h:commandButton>
      </p>
      </td>
    </tr>
    <tr>
      <td>
     <p>This is the login panel footer</p>
     <composite:insertChildren />
      </td>
    </tr>
  </tbody>
</table>
</composite:implementation>
</body>
</html>

Coloquei o código todo, mas o mais importante é da linha 12 até a linha 41, que é onde definimos as características do nosso componente. Isso deve facilitar que ferramentas deem suporte aos nossos componentes, e também a quem for usar esses componentes, pois damos mais informações a respeito das propriedades que ele precisa. A documentação diz que em muitos casos será possível construir componentes sem prover essas informações, deixando mais parecido com o que é hoje, mas diz também que falta definir um limite de até onde pode ser feito um componente sem especificar esse "contrato de uso".

Uma coisa interessante que pode ser vista nesse exemplo é a exigencia de um método chamado loginAction com uma assinatura específica dentro do objeto que for passado no atributo model.

O código que usa esse componente pode ser visto a seguir

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<!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"
      xmlns:ez="http://java.sun.com/jsf/composite/ezcomp">
<h:head>
<title>The Simplest EZComp Demo That Could Possibly Work</title>
</h:head>
<h:body>
<p>Login Panel Component</p>
  <ui:debug hotkey="p" rendered="true"/>
<h:form>
  <div id="compositeComponent" class="grayBox" style="border: 1px solid #090;">
 
    <ez:loginPanel id="loginPanelInConsumingPage" model="#{bean}">
 
      <f:valueChangeListener for="username" binding="#{bean.useridValueChangeListener}" />
      <f:actionListener for="loginEvent" binding="#{bean.loginEventListener}" />
      <f:actionListener for="cancelEvent" binding="#{bean.cancelEventListener}" />
      <f:actionListener for="allEvents" binding="#{bean.allEventsListener}" />
 
      <f:facet name="header">
         <h:panelGroup id="headerFacetInConsumingPage">
          <h:outputText value="this is the header facet in the consuming page" />
         </h:panelGroup>
      </f:facet>
      <h:outputText id="childInConsumingPage" value="this is a child component in the consuming page" />
    </ez:loginPanel>
  </div>
<p><h:commandButton value="reload" /></p>
</h:form>
</h:body>
</html>

Nesse exemplo pode reparar da linha 18 a 21 que usamos os composite:actionSource definidos no componente como ganchos para pendurar nossos listeners.

Só lembrando que esses exemplos são em cima da EDR2 do JSF2, então tudo que foi visto aqui pode não ser igual ao que vai estar na versão final.