EJB Stateful - Problema com a sessão

Pessoal,

Estou estudando EJB pelo livro “EJB em Ação”.
O livro me diz que os EJBs Stateful (com estado) conseguem manter a sessão com cliente.
Então fui fazer uma aplicaçãozinha pra testar isso.

Então criei um EJB Stateful com uma variável de instancia primitiva que irei incrementar a cada requisição apenas para ver como se comporta a sessão.
Coloquei três métodos de negócio, cada um incrementa a variável e a imprime no console. O ultimo método com a anotação @Remove.
Mais dois métodos como Listeners com as anotações “@PostConstruct” e “@PreDestroy” para imprimir no console quando um EJB é criado e quando é distruido.

Criei um cliente WEB com três Servlets, cada uma responsavel pela chamada de um método do meu EJB.
Quando chama a primeira Servlet ela executa a chamada do primeiro método do meu EJB e logo em seguida renderiza uma página com a o form action apontando para a próxima Servlet e assim por diante.

Problema:

Executo a primeira Servlet ela faz a chamada para o primeiro método do meu EJB, o método anotado com @PostConstruct executa normalmente, a váriavel de instancia é incrementada e impressa no console e a servlet escreve no response uma nova página que contém um form action que redireciona para a proxima Servlet.
Quando faço a chamada para a próxima Servlet pelo form que a ultima Servlet criou, ela faz a chamada para o segundo método do meu EJB só que o método anotado com “@PostConstruct” é executado novamente e a váriavel de instancia é impressa com o valor incrementado após seu valor default.

Então com isso percebi que outro EJB estava sendo criado para o mesmo cliente.
Será que isso tem algo haver por que eu estou fazendo a chamada para esse EJB em Servlets diferentes?
Servlets diferentes são clientes diferentes?

Estou sem entender o que está acontecendo, caso alguém possa ajudar aí eu ficarei muito agradecido :slight_smile:

Segue os códigos para qualquer coisa…

EJB:

@Stateful
public class CaminhoClientBean implements CaminhoClient {

	private int passo = 0;
	
	@PostConstruct
	public void onLoad() {
		System.out.println("Iniciei.");
	}
	
	@PreDestroy
	public void onDestroy() {
		System.out.println("Fui destruido.");
	}
	
	@Override
	public String passoUm() {
		return "Passo: " + ++passo;
	}
	
	@Override
	public String passoDois() {
		return "Passo: " + ++passo;
	}

	@Override
	@Remove
	public String passoTreis() {
		return "Passo: " + ++passo;
	}
}

Servlets Clientes:

public class ServletUm extends HttpServlet {
	
    @EJB
    CaminhoClient caminho;
    
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		response.setContentType("text/html");
		PrintWriter p = response.getWriter();
		p.println(caminho.passoUm());
		System.out.println(caminho.passoUm());
		p.println("<form action='/EJBzaoWebClient/ServletDois' />");
		p.println("<input type='submit' value='Passo 2' />");
		p.println("</form>");
		p.flush();
	}
}
public class ServletDois extends HttpServlet {

	@EJB
        CaminhoClient caminho;

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		response.setContentType("text/html");
		PrintWriter p = response.getWriter();
		p.println(caminho.passoDois());
		p.println("<form action='/EJBzaoWebClient/ServletTres' />");
		p.println("<input type='submit' value='Passo 3' />");
		p.println("</form>");
		p.flush();
	}
}

Opa,

Seguinte, acho que um dos problemas acontece pq vc cria uma instância ejb em cada servlet. O application server tem um pool de ejbs statefull,
com uma quantidade definida na configuração do server, ao declarar uma instância nova nos servlets o server esta criando novos objetos do seu session bean statefull e retornando para os servlets.

Reparei que vc não criou uma sessão com o cliente. É necessário criar uma HttpSession no ServletUm para que o container saiba que as requisições estão vindo do mesmo cliente.

Testa com a Session e vê o que acontece.

ahhhhhhhhhhhhhhhhhhhhhh…Outro erro que pode estar influenciando é o método “Treis” :twisted: :wink: :lol:

cara, será q não precisamos procurar melhor sobre o funcionamento da injeção @EJB ?

será q não tem nenhuma configuração faltando pra indicar se ele deve procurar em uma session existente? :S ( estranho o q eu falei, mas sei lah né ahuahua)

Cara, acho que os beans com estado mantém a sessão mais você que deve manter essa refencia.
Vi em alguns tópicos aqui no GUJ que o pessoal guarda no HttpSession.

nosaaa hauah

verdade … fiqueium tempo pensando … só hoje fui faze rum teste e entendi isso ae ahuhu

Faz sentido…
Então quer dizer que se eu fizer o lookup de um stateless session bean e manter a sua referência mesmo assim a cada requisição pode ser que o servidor me execute em outra instância?

Exatamente! Funciona assim,

Quando vc faz o lookup de um EJB, seja ele Stateless ou Stateful,
vc na verdade recebe uma classe proxy e não a instância em si.
(Mais sobre classes proxy aqui).

Quando o EJB que vc requeriu é Stateful, o proxy mantém a mesma
referência e, dessa forma, garante que cada nova chamada à um método
de nogócio será executado pela mesma instância até que vc chame o
método anotado com @Remove.

Quando o EJB é Stateless, a cada chamada de método o proxy pega
uma nova instância do cache de EJBs. Dessa forma o código:

meuEJBStateless.metodo1();
...
meuEJBStateless.metodo2();

pode ter instancias diferentes executando os metodo1() e metodo2()
(improvavel, mais possivel).

É por isso que o EJBs Stateless não tem estado, porque seria inutil
manter estado em um objeto se na próxima interação com esse EJB
uma instância diferente (e portanto com um estado diferente) for usada.

Cara, se você tem 3 @EJB em 3 servlets diferentes, são 3 stateful beans diferentes. HTTP é um protocolo “sem estado”, não importa que é o mesmo cliente chamandos os 3 servlets, ele nunca vai saber disso. E mais, injetar Stateful Session Beans em Servlets não é uma idéia muito viável, pois uma única instância de um tipo de Servlet existe em um container e muitas threads vão compartilhar esse bean, o que não deveria acontecer.

sim