sexta-feira, 18 de julho de 2008

Sobre os contextos do JBoss Seam

O primeiro passo para entender o conceito de bijeção (bijection) do JBoss Seam é compreender muito bem os contextos Web e os próprios contextos do JBoss Seam.

Os contextos do Seam são muito semelhantes aos contextos de uma aplicação Web baseado em Java EE (componentes Servlet e JSP). Os contextos são áreas de objetos onde podemos associar um nome (uma string) para identificação de um objeto ao armazená-lo neste contexto. Um analogia bem simples e grosseira seria imaginar um contexto como um Map da API de Collections do Java. Um Map permite associar uma chave (referência ou identificador) a um valor (outro objeto) para localização simples e rápida dos objetos (valor). Contudo, os contextos vão além de um simples MAP e determinam um tempo de vida para os objetos que ali são armazenados. O tempo de vida determina um escopo de existência dos objetos ao torná-los disponíveis para os Servlets e JSP's da aplicação.

Numa aplicação Web simples baseada nas tecnologias Servlet e JSP (ao usar Struts, WebWork e JSF não seria diferente) temos disponíveis os seguintes contextos para armazenar objetos: page, request, session e application.

Escopos Java EE para Web:
  • page - Objetos armazenados neste escopo tem o tempo de vida de apenas o tempo de execução (processamento) de uma página JSP no lado do servidor. Após a geração do conteúdo HTML pelo JSP no lado do servidor e o envio deste conteúdo para o browser, as referências para os objetos armazenados neste contexto são perdidos. Podemos imaginar este contexto como o escopo de um método onde declaramos váriaveis locais que são destruídas ao final da execução do método.

  • request - Objetos armazenados neste escopo tem o tempo de vida de uma requisição Web realizada por um usuário através do browser. Este escopo passa a existir no momento que o servidor atende a requisição e será destruído ao final da requisição Web quando o conteúdo gerado for enviado para o browser. Este contexto permite armazenar objetos que podem ser passados de um servlet para outro ou de um servlet para uma página JSP se ocorrer um encaminhamento (forward) de um componente para outro enquanto a requisição ainda está em processamento no lado do servidor. Podemos imaginar este contexto como o escopo determinado pela passagem de parâmetros quando um objeto invoca o método de outro objeto.

  • session - O contexto de sessão define uma área de objetos no lado do servidor para cada usuário que acessa a aplicação. Este contexto permite armazenar objetos com o mesmo nome para usuários diferentes e existe um isolamento para cada usuário não acessar objetos de outro usuário. Objetos armazenados neste contexto tem um tempo de vida maior que o tempo de vida determinado pelo contexto request e mantem os objetos no lado do servidor enquanto o usuário usar a aplicação Web. Após um período de inatividade do usuário, este contexto será destruído para eliminar somente os objetos deste usuário.

  • application - Este contexto define uma área de objetos que tem o tempo de vida da própria aplicação. Quando a aplicação Web entra no ar no servidor de aplicações o contexto application é criado e será destruído somente quando a aplicação for desligada ou o servidor de aplicações sair do ar. Objetos armazenados neste contexto serão compartilhados entre todos os usuários que acessam a aplicação. Este objetos estarão disponíveis para qualquer Servlet e JSP da aplicação Web.
O JBoss Seam define novos contextos para complementar os contextos da Web. Os contextos definidos pelo Seam são tratados como novos contextos e distintos dos contextos do Java EE Web. Contudo, alguns contextos do Seam usam internamente os mesmos contextos do Java EE Web (ou seja, podemos tratar como a mesma coisa!). O Seam disponibiliza os seguintes contextos: stateless, event, page, conversation, session, business process e application.

Escopos do JBoss Seam:
  • stateless - Contexto para armazenar a referência para objetos stateless (ex. session stateless EJB). Tecnicamente não é um contexto já que cada busca ou chamada a método num objeto resulta numa nova referência para o bean. Este bean é gerenciado pelo pool de beans do EJB Container.
  • event - Este contexto é equivalente ao contexto request da Web. Recebe outro nome porque é utilizado em outras situações além de requisições Web como, por exemplo, invocação RMI ou invocação via Seam Remoting aos componentes Seam. Este contexto está associado ao ciclo de vida JSF. Durante o processamento do ciclo de vida os objetos associados estarão disponíveis e serão eliminados somente ao final do ciclo de vida JSF. Inclusive este contexto tem o tempo de vida de uma requisição Ajax realizada pelas bibliotecas de componentes visuais JSF com suporte a Ajax.
  • page - Este contexto permite armazenar objetos que são criados numa tela JSF e estão disponíveis para esta tela. Este contexto pode ser comparado com o contexto page da Web. Contudo, um objeto armazenado neste contexto estará diponível somente para a tela JSF correspondente que o instanciou a partir de algum listener de evento. Como a documentação sugere, este contexto é útil em alguns componentes visuais como, por exemplo, numa lista clicável de opções que precisa de um objeto com valores específicos para esta lista e tela JSF. Objetos neste contexto estarão disponíveis mesmo em sucessivas requisições Ajax a partir de componentes visuais a partir do browser.
  • conversation - Aqui está o verdadeiro diferencial do JBoss Seam que agrega valor no desenvolvimento de aplicações Web com JSF. Este contexto permite armazenar objetos que terão um tempo de vida maior que uma requisição (event) e menor que uma sessão (session). Com este contexto se torna possível definir um escopo para objetos que são usados para implementar um fluxo de telas que realizam um "caso de uso" ou "história de usuário". Num Seam Component é possível anotar o início da conversação num método e anotar outro para demarcar o fim da conversação. O curioso deste contexto é que permite abrir várias janelas de browser para uma mesma tela JSF e cada janela representar uma conversação diferente (contexto diferente de objetos) para execução simultânea do mesmo caso de uso. Cada janela é um contexto separado que não influência um outro contexto aberto.
  • session - Este contexto é equivalente ao contexto Web de mesmo nome. Este contexto define um escopo para manter objetos como, por exemplo, o usuário logado ou outros objetos a serem compatilhados entre várias conversações (escopo de conversação), Além de objetos globais do usuário que acessa a aplicação.
  • business process - Define um contexto para objetos que são associados a um processo de negócio gerenciado pelo jBPM. Objetos armazenados neste contexto pertencem a um processo e podem ser compatilhados entre vários usuários que acessam o mesmo processo de negócio. Só faz sentido usar este escopo se a máquina de processos implementada pelo jBPM estiver em uso numa aplicação Web em conjunto com o Seam.
  • application - Este contexto é equivalente ao contexto Web de mesmo nome. Objetos armazenados neste contexto estarão disponíveis para todas as converssações e usuários que acessam a aplicação. Este contexto é útil ao armazenar objetos com conteúdo que não mudam com freqüência para compatilhar para toda aplicação como, por exemplo, configurações ou meta informações. Muitos objetos internos do próprio Seam são armazenados neste contexto.
Os contextos indicados acima definem namespaces, já que uma string é usada para dar nome ao objeto armazenado. Como cada contexto é isolado, torna-se possível usar o mesmo nome em contextos diferentes. Os nomes para os objetos armazenados nestes contextos são chamados de context variable. Numa tela JSF, ao usar uma expression language (EL) para associar um componente visual a um objeto (seam component) armazenado num destes contextos, o Seam realiza a busca do objeto a partir do nome indicado na expressão numa ordem de prioridade (event -> page -> conversation -> session -> business process -> application). Por exemplo, ao usar a EL #{usuario.nome} resultará numa busca pelo objeto associado ao nome "usuario" nos contextos na ordem indica e irá parar a busca ao encontrar a primeira ocorrência. Também é possível indicar programaticamente (dentro de um método do Managed Bean ou Seam Component) qual contexto deve obter o objeto sem realizar uma busca nos vários contextos.

5 comentários:

Rafael Ponte disse...

Muito interessante Spock! Uma das maiores dúvidas que vejo em relação ao Seam está relacionada aos escopos conversacionais.

O post é bem claro e sucinto. Parabéns.

Pica Pau disse...

Otima exposição! Completo e objetivo. Valeu a leitura. :p

guga disse...

Voçê poderia dar um exemplo de como colocar um objeto Usuario do escopo de sessão do Seam. por exemplo setar no momento do login um objeto Usuari no escopo de sessão?

Dr. Spock disse...

Olá Guga,

Você pode ejetar na sessão web do usuário um atributo privado de um bean usando a anotação @Out. Um local para fazer isso é a classe Authenticator criado pelo wizard do JBoss Tools (plugin) no Eclipse. Esta classe é usada no processo de autenticação realizado pelo JBoss Seam. Exemplo:

@Out(scope=ScopeType.SESSION)
private Usuario loggedUser;

Durante a autenticação preencha esse atributo com uma referência válida de usuário. Daí o Seam se encarrega de colocar esse objeto na sessão web.

romulotkd disse...

Dr. Spock, sei que este poste já é antigo, mas teria como você mandar um exemplo com o scopo de conversation.
meu email é romulotkd@gmail.com