Esse artigo faz parte da revista Java Magazine edição 54. Clique aqui para ler todos os artigos desta edição 

com uma poderosa combinação de frameworks.

À medida que as aplicações web crescem em complexidade, cresce também o número de casos de uso cuja inserção de dados é feita através de formulários que se expandem em várias páginas. Um exemplo comum desse tipo de caso de uso é a realização de compras em uma loja virtual, pois para comprarmos precisamos fazer uma busca por produtos, escolher o produto desejado e adicioná-lo ao "carrinho de compras". Se estivermos satisfeitos com os itens do carrinho, finalizamos a compra, senão voltamos ao início para buscar novos produtos. Para finalizar a compra é necessário verificar se o usuário possui cadastro, se está logado, qual a forma de pagamento escolhida, etc. Enfim, há um grande número de possibilidades de interação do usuário com a aplicação para que o caso de uso seja finalizado.

O problema desse tipo de caso de uso é a dificuldade de gerenciamento de fluxos de navegação complexos em aplicações web. Isso deve-se principalmente ao fato de que essas aplicações funcionam com base no protocolo HTTP (Hypertext Transfer Protocol), sendo limitadas aos recursos oferecidos por ele.

Este artigo tem como objetivo mostrar os problemas relacionados à construção desses fluxos de navegação e explicar como os frameworks Spring, JavaServer Faces e Spring Web Flow podem solucioná-los. Para demonstrar a utilização dos frameworks citados iremos construir uma pequena aplicação web para jogar Black Jack, um jogo de cartas também conhecido como “21”.

Os desafios do HTTP

O protocolo HTTP é baseado em um mecanismo de requisição e resposta: o cliente envia uma requisição solicitando algum recurso e então o servidor envia ao cliente uma resposta com o recurso solicitado (ou uma informação de erro). Essa comunicação é feita sem manutenção de estado, ou seja, as informações enviadas através de uma requisição não são mantidas em requisições futuras.

Para contornar as características inerentes ao fluxo “stateless” da comunicação entre clientes e servidores sob o protocolo HTTP, os containers web criaram meios para manter objetos em memória sob diferentes escopos. A definição destes escopos está diretamente ligada ao tempo de vida do objeto no container e sua disponibilidade para uso da aplicação.  Os escopos disponíveis são:

·         Page: contém dados que são visíveis apenas dentro de uma página. Se uma página contém dados no escopo page, as páginas inseridas nela com [1] ou redirecionadas a partir dela com não são capazes de enxergá-los;

·         Request: a informação nesse escopo é criada após a requisição e existe até que a resposta ao cliente seja completada;

·         Session (Sessão): os dados no escopo session são criados algum tempo após a primeira requisição do usuário e são descartados depois de algum período de inatividade, por exemplo, quando o usuário fecha o navegador ou quando a sessão é invalidada pela aplicação. Um dado em session sobrevive a vários requests;

·         Application: contém dados disponíveis a todos os clientes da aplicação e que estão disponíveis até que o servidor web seja reiniciado.

Para muitos problemas esses escopos são suficientes, mas os fluxos de navegação mais complexos não se encaixam naturalmente neste cenário. Como eles se expandem em várias páginas, é necessário gerenciar o ciclo de vida dos objetos manipulados pelo caso de uso, criando-os quando forem necessários e removendo-os quando o caso de uso acabar ou não necessitar mais dos mesmos. Dessa forma, os escopos page e request não se fazem suficientes, e a longevidade dos escopos session e application não é necessária, já que os dados armazenados nesses escopos continuam existindo após a finalização do caso de uso. Os usuários também podem utilizar diferentes casos de uso por vez, ou mesmo executar o mesmo caso de uso várias vezes. Em tal cenário, os dados associados com a execução de um caso de uso precisam ser limpos antes que uma nova execução seja iniciada. Resumindo, não existe um escopo que suporte casos de uso que utilizam múltiplas páginas.

Uma solução comumente adotada para resolver este problema é armazenar os dados em sessão e limpá-los manualmente após o fim do caso de uso. Apesar de a princípio parecer uma solução simples e eficiente, existem muitos problemas associados ao armazenamento em sessão:

·         Como gerenciar o balanceamento de carga em ambientes em cluster?

·         Qual o impacto no uso de memória do servidor com todos esses objetos em sessão?

·         E se o usuário quiser utilizar várias janelas ou abas para trabalhar paralelamente na realização de um caso de uso?

·         Que tipos de problemas poderiam ocorrer se o programador esquecer de retirar os objetos de sessão?

Outros problemas comuns associados ao controle de fluxo de navegação são:

·         Como garantir que os passos do caso de uso sejam executados pelo usuário na ordem correta?

·         Como prevenir que o usuário submeta os dados mais de uma vez?

·         Como tratar o fato de o usuário querer voltar um passo do caso de uso através do botão Voltar do navegador?

Para resolver todas as questões citadas anteriormente, foi criado o Spring Web Flow. Trata-se de um módulo do Spring Framework cujo objetivo é proporcionar uma solução para o controle de fluxo entre páginas de aplicações web através da implementação de um novo escopo para armazenamento de dados chamado de conversation (conversação). Com tal módulo torna-se possível criar os fluxos de navegação de uma aplicação web de uma maneira clara e simples. Além disso, é possível reutilizá-los em vários ambientes de desenvolvimento, em conjunto com frameworks como Struts, Spring MVC, Tapestry ou JavaServer Faces.

Criando a aplicação exemplo

A aplicação que será utilizada como exemplo deverá possibilitar o usuário jogar Black Jack contra o computador. O Black Jack deve ser disputado entre uma banca (que distribui as cartas) e um ou mais jogadores. Inicialmente, o jogador e a banca recebem duas cartas, mas apenas uma das cartas da banca deve ficar visível. Eles devem decidir entre puxar cartas do baralho ou parar, de modo que a soma dos valores das cartas em suas mãos não ultrapassem 21. O ganhador é aquele cuja soma ficar mais próxima de 21. Se as somas forem iguais, o jogo termina empatado. Os valores de cada carta são os seguintes: Rei, Rainha e Valete valem 10; as cartas com números valem o número correspondente; o Ás pode valer 1 ou 11, dependendo do que for mais conveniente no momento. Se o jogador tiver um ás e um 9, a soma das duas cartas é 20, se o jogador tiver um Ás, um 5 e um 8, o ás valerá 1, pois caso contrário a soma das cartas do jogador passaria de 21.

O fluxo de navegação do problema acima não é tão complexo, mas é completo o suficiente para que o funcionamento do Spring Web Flow possa ser mostrado.

·         Inicialmente o usuário será direcionado a uma página que perguntará o seu nome;

·         Após digitar o nome, o usuário clicará em um botão Iniciar um novo jogo, para ser então redirecionado a uma nova página. Essa segunda página mostra as cartas do jogador e da banca, além de dois botões: Puxar e Parar;

·         Caso o botão Puxar seja clicado, uma nova carta será adicionada à mão do jogador e a próxima página a ser mostrada irá depender da soma dos valores das cartas;

o        Se o usuário tiver uma mão com menos de 21 pontos, ele continuará na mesma página; ...

Quer ler esse conteúdo completo? Tenha acesso completo