Muita gente quando descobre que o Wicket guarda tudo na sessão do usuário, rapidamente lança duas perguntas de grande peso:
- Guardar tudo no HttpSession não estourará a memória do servidor?
- Não ficará lenta a replicação destes objetos entre os nodes de um Cluster?
Antes de continuar, explicarei agora os motivos que levam o Wicket a funcionar desta forma (enxer a memória). Se você já sabe, então pule dois parágrafos. :-)
O Wicket armazena na HttpSession as instâncias da página primeiramente para que quando você, desenvolvedor, construir uma página ou um componente, sinta-se num ambiente verdadeiramente Orientado a Objetos. Diferentemente dos frameworks action-based, aqui é possível manter referências de objetos, variáveis locais e de instância, declaradas sem qualquer problema pois quando o usuário clicar, o objeto que receberá o evento, é o mesmo que foi criado no início. Ótimo não? Sem dúvida! Não precisa-se saber de "parâmetro do request" ou qualquer outra tranqueira Web. É Swing-like, Java puro, POJOs e até parece desktop de verdade.
Pois bem, com os objetos na HttpSession, foi possível implementar algo ainda mais bacana: suporte ao backbutton do browser. Cada vez que o usuário submete alguma coisa a um componente, o estado deste componente é guardado na Session, como uma versão. É como se fosse feito um snapshot. Se o usuário clicar em voltar, e submeter alguma coisa novamente, o Wicket tem como saber que alguma coisa ali está diferente e poderá evitar problemas como submissão duplicada de registro, ou atualização indevida de dados já existentes. Maravilha!
E então você pensa: "que ótimo, Wicket me dá várias funcionalidades a troco de uma sessão de 15Megabytes por usuário". É verdade que se não houver um cuidado, a sessão pode explodir sim. Aqui vão algumas regras então para minimizar os riscos de uma HttpSession Shrek:
- Utilize LoadableDetachableModels para prover muitos dados aos componentes
- Não deixe cair na sessão objetos não serializáveis
- Utilize o SecondLevelCacheSessionStore
Agora, a parte que nos interessa: Wicket's Garbage Collector!
O "lixo" que o Wicket precisa remover é basicamente, versões antigas das páginas/componentes criados pela navegação do usuário no site. É possível, mas pouco provável, que o usuário clique num Voltar e tente fazer algo. Por isso o framework elimina versões muito antigas (o padrão é manter somente as últimas 5 em deployment mode). Mas e se o desenvolvedor quiser armazenar mais versões, sem comprometer a memória do servidor, e conseqüentemente proteger sua aplicação de ataques Denial of Service? Para isso serve o SecondLevelCacheSessionStore.
Sua utilização depende do arquiteto do sistema em definir no Application do Wicket, que o framework deve utilizá-lo. Porém, antes de apresentar o código, vamos entender como os objetos são persistidos. A persistência é feita por algum objeto que implementa a interface IPageStore. Este objeto será responsável por persistir - storePage() - seja em banco de dados, arquivo flat ou qualquer outro meio, os objetos que o framework considerar não mais necessários na sessão, ou quando o usuário precisa ver algum dado mantido em versão anterior de algum componente - getPage().
A implementação padrão para a interface IPageStore, é a classe DiskPageStore. Nela, ocorre o processo de serializar as páginas e toda a árvore de objetos ali contida, para posteriormente salvar no disco do servidor. Problema de memória gigante resolvido! Mas... pera. Você possui um cluster? Hmm... Será que o Wicket vai funcionar sem problemas ali? Se ele arquivar as páginas no disco de um servidor, o que acontecerá em outro nó do cluster?!
Wicket em ambiente Clusterizado
A grande sacada...
Serialização é um processo complicado e lento. Não falei antes, mas é importante lembrar que todos os objetos que ficarem na HttpSession, devem implementar Serializable. Isto é importantíssimo; se houver um não serializável, o Wicket vai gritar no log. Agora, se tudo correr bem, os objetos serão serializados e persistidos no disco pelo objeto DiskPageStore, estando o aplicativo Wicket em cluster ou não.
Num ambiente Cluster regular - que segue os padrões das configurações simples e funcionais (ex: Tomcat em Cluster) - o framework inteligentemente coloca as páginas já serializadas no processo de replicação, para que quando o objeto HttpSession chegar no outro nó, não há perda de processamento em realizar a tarefa de serialização duas vezes. As páginas também não são de-serializadas; vão direto para o DiskPageStore que as armazena para uso futuro, se necessário. Este mecanismo é o que garante a performance do framework neste tipo de deploy.
Espero poder ter desmistificado agora a idéia de que Wicket estoura com a memória dos servidores, nem tão pouco que não suporta ambientes clusterizados.
Hands-On: Configurar a aplicação para utilizar SecondLevelCacheSessionStore
Mão na massa...
Para encerrar, veja como é simples configurar o SecondLevelCache com o DiskPageStore:
public class MyApp extends WebApplication { @Override protected ISessionStore newSessionStore() { return new SecondLevelCacheSessionStore(this, new DiskPageStore()); } }E agora, que tal? É fácil ou não é?! :-D
PS: o Anjo Negro ataca novamente... desta vez, com gol de letra!
[]'s!