Utilizando o @ConversationScoped no VRaptor 4

O VRaptor em sua versão 4 utiliza o CDI 1.1 como base. E uma das considerações a serem feitas quando usamos o CDI é o escopo de vida dos objetos. Sempre é preciso escolher com cuidado o escopo, levando em consideração qual vai ser a aplicação do objeto.
Utilizar o @RequestScoped onde é necessário um @ApplicationScoped pode ser desastroso. Um escopo muito util nesse cenário é o @ConversationScoped que nos permite um controle maior conforme a nossa necessidade.
A aplicação apresentada nesse tutorial está disponivel no GitHub: ScoreCDI.
Abaixo nosso Controller:

package br.com.angeliski.controller;

import java.io.Serializable;
import java.util.Random;

import javax.enterprise.context.Conversation;
import javax.enterprise.context.ConversationScoped;
import javax.inject.Inject;

import br.com.caelum.vraptor.Controller;
import br.com.caelum.vraptor.Get;
import br.com.caelum.vraptor.Post;
import br.com.caelum.vraptor.Result;

@Controller
@ConversationScoped
public class HomeController implements Serializable {

	private static final long serialVersionUID = 943045823176068998L;
	private Result result;
	private Conversation conversation;
	private Integer resultado;
	private int total;

	/**
	 * @deprecated CDI eyes only
	 */
	protected HomeController() {
		this(null, null);
	}

	@Inject
	public HomeController(Result result, Conversation conversation) {
		super();
		this.result = result;
		this.conversation = conversation;
	}

	@Get
	public void index() {
	}

	@Get
	public void game() {
		if (conversation.isTransient()) {
			conversation.begin();
		}
		result.include("cid", conversation.getId());
		gerarPergunta();
	}

	private void gerarPergunta() {
		Random random = new Random();
		Integer primeiroValor = random.nextInt(100);
		Integer segundoValor = random.nextInt(100);

		resultado = primeiroValor * segundoValor;

		result.include("primeiro", primeiroValor);
		result.include("segundo", segundoValor);
	}

	@Post
	public void game(Integer resposta) {
		if (resultado.equals(resposta)) {
			total++;
		}
		gerarPergunta();
		result.include("cid", conversation.getId());
	}

	@Get
	public void fim() {
		if (!conversation.isTransient()) {
			conversation.end();
		}
		result.include("total", total);
	}

}

A primeira distinção desse Controller é a anotação @ConversationScoped na classe. Isso indica para o CDI qual vai ser o escopo, mas cuidado: só isso não é suficiente para que a sua classe tenha um escopo maior que o escopo request.
Para que o escopo se torne persistente, não perdendo os dados a cada requisição, é necessário injetar um objeto Conversation e chamar seu método begin. Isso pode ser observado aqui:

        @Get
	public void game() {
		if (conversation.isTransient()) {
			conversation.begin();
		}
		result.include("cid", conversation.getId());
		gerarPergunta();
	}

Nesse método a conversação é iniciada. O Conversation tem um estado padrão conhecido como *transient*. Quando o método begin é acionado ele passa para o *long-running* mantendo os dados até que seja invocado o método end do Conversation.
Só que um detalhe importante deve ser observado. O CDI não tem como saber a qual objeto você está tratando. O único modo dele saber isso é você informando qual o identificador daquela conversação e isso é feito através do pârametro **cid**.
É necessário informar na url esse pârametro para que o CDI consiga recuperar o objeto correto. Observe que o pârametro cid é incluido no result para que seja possível utilizar ele na pagina.
O nome utilizado para incluir o *id* do Conversation no result não é fundamental. Ele poderia ser qualquer um, desde que fosse recuperado corretamente na pagina. Só é fundamental utilizar o pârametro **cid** quando a requisição for enviada para o servidor.
Observe como ficou a nossa pagina que vai utilizar esse parâmetro.

<%@ taglib prefix=&quot;fmt&quot; uri=&quot;http://java.sun.com/jsp/jstl/fmt&quot;%>
<%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot;%>

<!DOCTYPE html>
<html>
<head>
<title>Score CDI</title>

</head>
<body>




<h4>Sessão atual ${cid}</h4>






Quanto é ${primeiro} * ${segundo}?





<form action=&quot;${linkTo[HomeController].game}?cid=${cid}&quot; method=&quot;POST&quot;>
		<input name=&quot;resposta&quot; type=&quot;number&quot; required>
		<button type=&quot;submit&quot;>Continuar jogo!</button>
	</form>





	<a href=&quot;${linkTo[HomeController].fim}?cid=${cid}&quot;>Finalizar!</a>

</body>
</html>

Você pode observar que a action do formulário faz o uso correto do **cid**. Quando essa requisição for enviada ao servidor, vai enviar junto o identificador correto.
Enquanto a requisição for feita enviando o **cid** o número exibido em Sessão atual vai continuar o mesmo, pois a conversação vai se manter. Você pode fazer um teste acessando diretamente ‘/home/game’ sem enviar o cid no pârametro.
Vai ser iniciada uma nova conversação e o cid irá se modificar.

Por fim temos o método que encerra o *long-running* através do método end:

	@Get
	public void fim() {
		if (!conversation.isTransient()) {
			conversation.end();
		}
		result.include(&quot;total&quot;, total);
	}

Aqui é valido observar que caso você não encerre uma conversação, ela não vai se manter em memória por muito mais que dois minutos, que é o tempo padrão. Isso garante que diferente do @SessionScoped os recursos vão ser liberados antes, caso a aplicação não encerre o estado daquele objeto.
O timeout pode ser ajustado através do método setTimeout conforme a necessidade, mas normalmente não é preciso.

Duvidas? gostou? Me acha um idiota?

Comenta ai!!

Angeliski

Anúncios

Comenta ai !

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s