quinta-feira, 10 de dezembro de 2009

Seam, Richfaces e Ajax: Conversação para ligar tudo isso.

Desde que comecei a utilizar o Seam sempre fiquei um tanto quanto decepcionado por não conseguir utilizar Ajax para fazer interações básicas com o usuário, tal como o usuário clica em um botão para adicionar algo (uma pessoa) a uma lista, abre-se então uma janela com uma listagem de pessoas que ele pode selecionar e através de um filtro ele pode digitar o nome desta pessoa para limitar o número de resultados. Ao encontrar a pessoa desejada o usuário a seleciona e esta é adicionada a uma lista do sistema que representa algo.

A figura acima mostra um caso típico deste tipo de interação. Este tipo de interação torna muito mais prático a utilização do sistema por parte do usuário, já que o tempo de resposta de cada requisição do usuário é menor.

A pouco tempo atrás comecei a escrever uma nova aplicação onde trabalho, criando um projeto war com o Seam 2.2.0.G.A, o que já me deixou bastante motivado, pois dá primeira vez que tentei criar um projeto desta forma não obtive sucesso, pois tentei utilizar os plugins do Eclipse para criar o projeto, sendo que este simplesmente não fazia deploy das alterações que eu realizava. Criei utilizando o seam-gen e depois importei o projeto no Eclipse e tudo fucionou como esperado, fazendo hot-deploy das classes, o que é um grande passo no desenvolvimento de aplicações para web utilizando Java.

Estava decidido que neste projeto iria utilizar Ajax de qualquer maneira. Após dois dias (infelizmente não trabalho só com desenvolvimento) testando diferentes maneiras de chamar meus componentes descobri o santo graal :D.

Mas vamos começar do começo, pois acredito que o Richfaces tenha problemas mesmo e que a forma como o utilizei apenas contornoram tal problema, mas até hoje não sei por quê. Entrei em contato com os desenvolvedores, mas até hoje não obtive resposta porque tecnicamente a aplicação não se comportava como eu esperava, sendo assim, gostaria de deixar o caminho que percorri, quem sabe alguma alma me explique por que não obtive sucesso inicialmente.

Bem, chega de enrolação e vamos para a prática. Criei um componente de listagem que estende a classe EntityQuery do framework Seam. Na minha janela de busca de pessoas liguei o "Nome" no "Filtro de Busca" ao nome de um campo que é utilizado no meu componente para restringir os resultados da pesquisa. Cada vez que uma letra era digitada na caixa de busca a listagem era re-renderizada. Isto funcionou muito bem, filtro feito!

O problema apareceu quando selecionei uma pessoa pela primeira vez. Quando renderizava a lista de pessoas, o link Selecionar chamava um método para adicionar a pessoa ao sistema passando o objeto que representava a pessoa como parâmetro. O problema é que na aplicação o parâmetro passado era simplesmente null. Já havia visto este problema, quando um colega estava brincando com uma solução parecida.

Comecei então a brincar com o escopo do meu componente. Quando coloquei ele no escopo de página o parâmetro começou a ser passado, porém quando utilizava o filtro e a listagem era re-renderizada o parâmetro que era passado era a referência para o objeto que representava a pessoa que estava na listagem logo que esta abria, ou seja, sem filtro nenhum.

Deve existir alguma explicação para este comportamento, porém não sei explicar qual é, apenas posso dizer que ao colocar a listagem no scope de conversação consegui resolver meus problemas, tudo funcionou perfeitamente, conseguia filtrar os resultados e clicar em um deles e o parâmetro passado a minha função era o objeto correspondente a pessoa a qual eu havia clicado, bingo! Ops, cedo demais para ficar tão feliz :(

Ao digitar muito rapidamente no filtro de busca o Seam gerava um erro: The conversation ended, timed out or was processing another reques. A solução foi bem simples, demorei apenas para encontrá-la: utilizar o componente Queue do Richfaces. Este faz com que as requisições Ajax sejam enfileiradas no browser e submetida apenas quando a requisição atual retornar. Isto evita que chegue uma requisição no servidor enquanto ele já está processando outra requisição durante uma conversação.

Só não fiquei satisfeito de colocar meu componente de listagem no escope de conversação por padrão. Para evitar isso criei um componente que cria e desinjeta tal componente no scope de conversação quando necessário.

Pronto, agora sou um programador feliz, posso utilizar Ajax na minha aplicação com Seam!