Posts Tagged ‘JavaServer Faces’

Limpando a árvore de componentes

Tuesday, June 7th, 2011

A natureza stateful do JSF nos ajuda em muitos cenários ao desenvolver nossas aplicações Web, cenários estes que não são tão simples assim de implementar com frameworks de natureza estritamente stateless, como a maioria dos frameworks action-like. Por outro lado, problemas que são facilmente solucionados ao se utilizar de frameworks action-like são, algumas vezes, extremamente difíceis de se resolver com frameworks component-based, como JSF.

Durante os treinamentos em JSF da TriadWorks, nós nos preocupamos em, juntamente com os alunos, desenvolver uma aplicação Web que explore cenários o mais próximo da realidade, trazendo diversos problemas à tona para que desta forma possamos escrever a solução mais adequada utilizando o framework. Entre estes problemas, existe um absolutamente incomodo, que é a limpeza da árvore de componentes, mais precisamente dos formulários - ocasionado pela natureza stateful da tecnologia, claro.

Há mais ou menos um mês o @DaniloMagrini pediu uma ajuda no Twitter sobre um problema que a equipe dele estava enfrentando, e o problema era justamente a limpeza da árvore de componentes. Um dos membros da equipe do Danilo, o @NeiAlcantaraJr, postou o problema no GUJ e explicou o mesmo de maneira simples e objetiva, sendo, eu acabei respondendo na própria thread.

Problema

A primeira vez que bati de frente com este problema foi por volta de 2006-2007, logo quando comecei a trabalhar com JSF e os componentes do Ajax4Jsf, e a partir daí já expliquei o mesmo inúmeras vezes na lista de discussão da #javasf - você pode encontrar as threads no histórico do grupo - porém, até hoje, não entendi o porquê de eu nunca ter blogado sobre o assunto.

Para não prolongar muito esse post tentando exemplificar como e quando o problema ocorre, eu vou pegar o gancho na thread do GUJ e somente apresentarei a solução aqui no blog - quase que um copy’n paste da minha resposta na thread, porém com mais alguns poucos detalhes.

Portanto, se você não conhece ainda o problema você pode ler o inicio da thread no GUJ. Alias, melhor ainda se você ler a thread inteira.

Mas para os preguiçosos de plantão, o resumo da novela é: nem sempre limpar os dados no managed bean é suficiente para limpar o formulário da página.

Componentes de input

Antes de irmos direto à solução, vale a pena revisarmos como os componentes de input são processados durante do ciclo de vida do faces, pois eles são um pouquinho mais complicados do que você pensa.

Os componentes de input (todos que implementam EditableValueHolder) possuem 3 (três) tipos de valores que são alterados durante o ciclo de vida de uma requisição, eles são:

  1. Submitted Value
  2. Local Value
  3. Value Binding (aka model value)

Esses 3 valores mudam nos componentes durante o ciclo de vida da seguinte maneira:

  • Quando você submete um formulário todos os inputs da página são submetidos como string (parâmetros de request) no corpo HTTP, e na APPLY REQUEST VALUES (2a fase) estes valores são setados nos seus devidos componentes de inputs como “submitted value”;
  • Após isso, na fase de validação (PROCESS VALIDATIONS - 3a fase), cada componente de input tenta converter e validar seu “submitted value”, em caso de sucesso o componente define seu novo valor convertido como “local value” e seta para null seu “submitted value”, continuando assim o ciclo de vida. Em caso de erro de conversão ou validação o componente não define seu “local value” e marca o componente como inválido, que por fim pula para última fase (RENDER RESPONSE);
  • Se não houver erro na fase de validação o ciclo de vida continua na UPDATE MODEL VALUES para popular o modelo (managed bean, entidades etc), ou seja, o “model value”. Para cada componente de input setado no modelo o seu “local value” é setado para null, daí o ciclo passa pela fase INVOKE APPLICATION e termina na RENDER RESPONSE exibindo os valores do modelo (através das EL’s);
Na última fase (RENDER RESPONSE), o componente pode retornar qualquer um dos 3 valores, contudo seguindo essa ordem de prioridade:
  1. Se houver submitted value então retorne-o;
  2. Caso contrário, se o local value é não nulo então retorne-o;
  3. Caso contrário, avalie a EL, ou seja, chame o getter do modelo;

Solução

Entender a prioridade acima é importante, pois assim você mata a charada do problema.

Esse problema independe do conjunto de componentes que seu projeto utiliza e ocorre muito comumente quando trabalhamos com a mesma árvore de componentes, ou seja, quando utilizamos muito AJAX e/ou navegação orientada a estados. Quando utilizamos a navegação padrão do JSF dificilmente isso ocorre!

Todas as soluções que conheço envolve limpar a árvore de componentes que está “suja”:

  1. Navegação padrão do Faces para recriar toda a viewroot (não vale retornar null);
  2. Limpar de maneira educada todos os componentes de inputs;
  3. Limpar os inputs de maneira “brute force” (parentComponent.getChildren().clear() - eles serão recriados na RENDER RESPONSE novamente, porém só funciona com JSF 1.x, pois o 2.x parece seguir corretamente a spec);

Na maioria das vezes eu me utilizo da segunda opção, a tal da “maneira educada” de limpar a árvore de componentes. Contudo, o @DaniloMagrini relatou que esta abordagem não funciona muito bem quando existem composite components na página. Eu sinceramente não lembro de ter tido esse problema, assim como também não pesquisei sobre o assunto. De qualquer forma, o Danilo melhorou a implementação do método cleanSubmittedValues para funcionar com composite components.

Tendo um dos cuidados acima antes de exibir o formulário ou parte dele (via AJAX) vai garantir que os componentes estejam limpos para que o usuário possa entrar novos dados.

No mais, segue alguns links para complementar o que eu disse:

Concluindo

Como podem ver, o problema está intimamente ligado a natureza stateful do JSF, e querendo ou não, nós temos que lidar com ela.

Se você estava apressado e pulou a thread no GUJ então eu aconselho novamente: leia a thread por completo, pois com certeza entender o problema, saber como soluciona-lo e principalmente como os componentes de input trabalham durante o ciclo de vida do faces vai te poupar muitas horas de dor de cabeça.

Managed Beans. Não complique, simplifique.

Thursday, August 27th, 2009

Já é sabido de todos que JSF é um framework web MVC com uma filosofia voltada a component-based. E não diferentemente dos bons frameworks action-based existentes hoje em dia, o JSF também se utiliza de POJOs como parte do controller.

Sim, eu estou falando dos, já muito conhecidos, managed beans. Eles são os responsáveis por intermediar a comunicação entre nossas páginas e o nosso modelo. Até aí tudo bem, não existe qualquer mistério, basta entender um pouco sobre MVC que isso torna-se óbvio.

Se você observar, quando está trabalhando com JSF, notará que cerca de 50-70% do seu esforço é dedicado na implementaçao do managed bean. E que as futuras alterações e correções de bugs estão intimamente ligados a ele. Implementar corretamente um managed bean pode evitar muita dor de cabeça e facilitar na manutenção da aplicação.

Mas você já se perguntou como e qual seria a melhor forma de implementá-los? Não que exista apenas uma única maneira de escreve-los, longe disso, mas em contra partida existem inúmeras maneiras de torna-los realmente ruins.

Escrever um managed bean de forma porca incorreta pode dificultar e muito sua vida e da sua equipe. Falta de legibilidade, dificuldade para escrever testes de unidade e principalmente medo de alterar o código são apenas alguns dos possíveis problemas.

Agora pare e imagine a vida do próximo desenvolvedor que deverá manter tal código. É, não parece algo divertido.

Managed Beans mais responsáveis

A principal responsabilidade de um managed bean é intermediar a comunicação entre as páginas (componentes do JSF) e nosso modelo. Escutar eventos, processa-los e delegar para a camada de negócios são apenas algumas de suas responsabilidades.

Talvez devido a experiência com frameworks action-based muitos desenvolvedores acabam subutilizando os managed beans e sobrecarregando as páginas com lógicas e dados desnecessários. Os managed beans e as páginas usam a abusam do modelo “pull” durante a renderização e (re)construção da árvore de componentes, isto é, basicamente as páginas é quem decidem (através de EL) quais os dados e lógica necessária para que possam ser processadas.

Graças a este modelo nós conseguimos retirar toda lógica de renderização da página e colocando-a onde jamais deveria ter saído, no managed bean. Esta lógica estará localizada nos métodos públicos do managed bean que serão acessados através de EL pela página.

Não importa se a lógica é simples ou complexa, é quase que mandatório que ela esteja localizada no seu managed bean. Então bizarrices lógicas de apresentação nas páginas, que não são difíceis de se encontrar por aí, como esta deveriam ser evitadas:

<h:panelGrid id="pnl" rendered="#{loginBean.usuario.admin == 1 or loginBean.permissao('exibir_painel_de_usuarios')}" />
	...
</h:panelGrid>

Reparem que a lógica de apresentação está, representada através de EL, na página. Se você teve a infelicidade de encontrar códigos assim então há grandes possibilidades de você também encontrar regras de negócio nas páginas, o que é muito pior que encontra-las no controller.

E olha que este foi apenas um exemplo simples e ligeiramente legível, já encontrei códigos mais complicados que este de dificil leitura e horrível de dar manutenção.

O problema disso é se houver a necessidade de mudar a regra (o que muitas vezes ocorre), mas e se ela estiver duplicada por dezenas de páginas, como você faria? Como testa-la? Pense um pouco sobre isso.

Imaginem um código um pouco pior que este (com 3 ou 4 condicionais), mas dentro de um componente de iteração (como h:dataTable ou ui:repeat). É, você nem vai querer imaginar.

A regra de visualização acima deveria está encapsulada no managed bean, a página não deveria conhecer a regra, mas apenas quem a conhece. Então pensando nisso nós poderíamos alterar nosso código para algo melhor, algo como isto:

<h:panelGrid id="pnl" rendered="#{loginBean.permitidoExibirPainelDeUsuariosLogados}" />
	...
</h:panelGrid>

O componente não conhece a regra, mas sabe exatamente a quem perguntar. O código está bem mais legível pois revela sua intenção. Agora está bem mais fácil mudar a regra (pois temos apenas um único ponto de manutenção) e principalmente testa-la através de testes automatizados.

Um managed bean deveria ao máximo tentar esconder a complexidade das páginas através de métodos que revelem a devida intenção, ou seja, a nomenclatura dos métodos é realmente importante e você deveria dar importância a legibilidade do seu código.

Comunicação entre managed beans

O que eu tenho para falar, de longe, se iguala ao que pode ser encontrado no excelente conteúdo sobre comunicação no JSF neste post no blog do BalusC, além de que para mim ele aborda 99% sobre como managed beans podem se comunicar.

O que pretendo falar está mais relacionado em como manter uma conversa (troca de mensagens) saudável entre managed beans. Em como explicitar os parâmetros esperados e como passar parâmetros de um managed bean para outro de maneira coerente.

A comunicacão entre managed beans não difere da troca de mensagens entre componentes de software na orientação a objetos. Cada componente (managed bean) conhece a interface pública do outro componente para que eles possam trocar mensagens.

Sendo, ter esta interface pública bem definida pode facilitar muito a “conversa” entre os managed beans, facilitar os testes, evitar problemas inesperados, diminuir o acoplamento, aumentar a coesão e assegurar contratos e invariantes do componente.

Nada mais claro que um exemplo para assimilar a idéia:

<h:dataTable var="item" value="#{compraBean.itensNoCarrinho}">
    <h:column>
        <h:commandLink value="detalhe" action="#{itemBean.exibirDetalhesDoItem}">
            <f:setPropertyActionListener value="#{item}" target="#{itemBean.itemSelecionado}" />
        </h:commandLink>
    </h:column>
</h:dataTable>

O código acima não tem nada de errado ou anormal, nada do que não estejamos acostumados a ver quase todos os dias. Se repararem há um pequeno “dialogo” entre dois managed beans - compraBean e itemBean - que estão trocando informações.

O problema não está na comunicação em si, pois isso teria que acontecer de um jeito ou de outro, mas sim em como eles se comunicam. O managed bean compraBean conhece detalhes de como o itemBean se comporta, ou seja, ele sabe mais do que o necessário.

Um maneira de melhorar isso seria ter uma interface pública bem definida no itemBean. Algo semelhante ao código abaixo:

<h:dataTable var="item" value="#{compraBean.itensNoCarrinho}">
    <h:column>
        <h:commandLink value="detalhe" action="#{itemBean.exibirDetalhesDoItem(item)}" />
    </h:column>
</h:dataTable>

Além de todos os benefícios (baixo acoplamento, alta coesão etc) citados acima, nós ganharíamos de cara maior legibilidade no código, o que é algo realmente importante quando a comunicação foge do trivial.

Vale ressaltar que independente da comunicação ocorrer na página (por meio da JBoss-EL) ou diretamente dentro do managed bean, essa abordagem poderia -e até deveria- ser utilizada. Para o segundo caso, frameworks como JBoss Seam ou Spring ajudam a injetar as dependências de maneira simples através de anotações.

Um ou vários managed beans?

Não tenho uma opinião realmente formada quanto a isso, ainda não. Já pensei várias vezes sobre o assunto, e sempre me pego vislumbrando cenários nada comuns. Que no caso os considero como casos excepcionais.

Desde 2006 eu trabalho com JSF, ministro cursos e treinamentos e presto consultoria para algumas empresas. E posso dizer que somente três vezes eu precisei ter mais de um managed bean para resolver uma tarefa. Dentre todas eu tenho a ligeira impressão que segui o caminho errado mais complicado, e que se tivesse pensado mais um pouco eu poderia ter evitado tais soluções.

Managed beans são componentes, em sua grande maioria, intimamente ligados a(s) página(s) e que deveriam ter apenas o estritamente necessário para representar a GUI. Dificilmente você terá um managed bean genérico (CRUD não conta) que poderia funcionar em todo lugar. Você precisa fazer um esforço hercúleo para conseguir isso, e as vezes parece que não vale a pena.

A idéia de um managed bean ter dados (entrada pelo usuário e estados) e comportamentos (eventos e regras ligadas a apresentação) juntos segue o princípio básico da OOP, o que torna o trabalho mais simples e coeso. Tentar ir contra isso (ao estilo ActionForm e Action do Struts) pode te trazer muita dor de cabeça a médio-longo prazo.

Neste post do Neil Griffin, um dos autores do primeiro livro de JSF2.0, ele sugere alguns “rótulos” para os diversos tipos de managed beans que podem haver numa aplicação, e ainda por cima ele incita que seria uma boa prática trabalhar com vários managed beans por questões de SoC, baixo acomplamento etc.

De fato, devido ao modelo “pull” podemos ter managed beans atuando como componentes menores, mais reutilizáveis e mais orientados a objetos. Ter componentes (managed beans ou não) de granularidade fina colaborando para construção da página é algo bastante comum, e eu mesmo me utilizo muito disso. Mas não são destes tipos de componentes que falo, muito menos o Neil.

Eu, particularmente, não concordo com quase nada no post do Neil. Para mim ele se utilizou de argumentos isolados e vagos para defender sua idéia. Managed beans por sua natureza deveriam ser simples, contudo a abordagem do Neil, definifitivamente, vai contra tudo isso. Eu a considero um overkill, e ela torna qualquer aplicação web difícil de manter.

Bem, se dois ou mais managed beans relacionados -delimitados a um contexto- estão servindo para você e sua equipe, ótimo. Continue assim, não tem porque mudar. O framework (JSF) te permite isso, contudo eu tenho sérias dúvidas se essa abordagem vale a pena.

Conclusão

Este post estava em draft faz um bocado de tempo, mas a falta de tempo e a coragem não me permitiam finaliza-lo. Felizmente resolvi tira-lo do baú e posta-lo.

Tudo que comentei aqui está mais ligado a minha experiência e o que aprendi durante estes anos como desenvolvedor (e conversando com outros profissionais) do que eu pude encontrar em livros de JSF sobre o assunto. Existem muitos livros bons, assim como artigos em blogs e sites especializados sobre o assunto, mas nada melhor que a experiência do dia a dia para nos mostrar caminhos mais adequados de como solucionar determinados problemas.

Por mais longo que o post tenha ficado, eu ainda sinto que falta complementa-lo em alguns aspectos e pontos. Pois o que falei acima acaba te levando a aderir outras soluções não menos importantes, mas que já foram bastante citadas neste blog e discutidas em listas de discussão.

Entre os três tópicos abordados aqui eu ainda tenho algumas dúvidas quanto ao último, principalmente depois desta resposta do @edburns a minha pergunta no Twitter. Hoje minha opinião está mais para o lado “um managed bean é suficiente” por não ter encontrado respostas que me convencessem do contrário.

Enfim, como disse antes, se vários managed beans intrinsecamente relacionados estão resolvendo teu problema por algum motivo, ótimo! Continue assim. Se você tiver um tempinho e puder comentar sobre tua decisão eu ficaria grato.