EJB Stateful perdendo estado quando injetado num Stateless

Boa noite a todos,

Estou com o seguinte problema…

tenho um EJB de Permissão que é Stateful, e N EJBs que são Stateless e que precisam acessar o Ejb de Permissão.

Quando eu faço o acesso ao ejb stateful na camada web (na minha action) ele recupera os dados normalmente, isso
é feito através de um service locator que coloquei, usando um map para pegar os ejbs.

Mas quando eu faço a injeção dentro de um ejb através do @EJB ele vem sem os dados do stateful, criando um
novo objeto vazio. Qual a melhor forma de resolver esse problema?

Você não consegue injetar um Stateful em um Stateless. A explicação é porque um stateless não guarda estado, então se você injeta um stateful em nível de classe você acaba compartilhando seu stateful. O ideal, nesse caso é fazer o lookup via JNDI ou service locator dentro do método.

valeu garcia-jj,

acabei de receber sua resposta, e é exatamente a solução que estava dando, colocando a chamada ao servicelocator dentro do método. Vou testar e posto os resultados.
Valeu!

Não funcionou. Continua criando uma instância, mesmo utilizando o serviceLocator (que é o mesmo código que uso na camada web para obter os ejbs)
E na camada web funciona legal, recuperando o stateful adequadamente.

Mais alguma sugestão?

Você poderia postar seus códigos ?

public class ServiceLocator {

	private InitialContext jndiContext;	
	private Map<String, Object> cache;
	private NameResolver nameResolver;
	
	private ServiceLocator(){
		try{
			jndiContext = new InitialContext();
			
			Map<String, Object> cRef = new HashMap<String, Object>();
			cache = Collections.synchronizedMap(cRef);
			nameResolver = new JBossNameResolver();
		} catch (NamingException ex){
			ex.printStackTrace();
		}
	}
	
	@SuppressWarnings("unchecked")
	public Object getRemoteEJB(Class ejbInterface) throws Exception {
		String jndiName = nameResolver.resolveNameForRemoteEJB(ejbInterface);
		Object result = cache.get(jndiName);
		if (result == null) {
			result = jndiContext.lookup(jndiName);
			cache.put(jndiName, result);
		}
		return result;
	}
	
	@SuppressWarnings("unchecked")
	public Object getLocalEJB(Class ejbInterface) throws Exception {
		String jndiName = nameResolver.resolveNameForLocalEJB(ejbInterface);
		Object result = cache.get(jndiName);
		if (result == null) {
			result = jndiContext.lookup(jndiName);
			cache.put(jndiName, result);
		}
		return result;
	}

	private static ServiceLocator instance;
	
	public static synchronized ServiceLocator getInstance(){
		if (instance == null){
			instance = new ServiceLocator();
		}
		return instance;
	}
}

No código abaixo o controleService chama o serviceLocator acima e é do tipo ControleService, que é stateful).
No caso abaixo o stateful funciona corretamente.

    public String salvar() throws Exception {
    	try {
    		inicializaSeguranca();
    		controleService.verificaPermissao("ACAO", "INCLUIR");

			acaoService = (AcaoService) serviceLocator.getLocalEJB(AcaoService.class);
    		acaoService.persistir(acao);

    		return SUCESSO;

		} catch (Exception e) {
			if (e instanceof AutorizacaoException){
				erro = false;
				erroMensagem = null;
				return RESTRITO;
			} else if (e instanceof PermissaoException){
				erro = false;
				erroMensagem = null;
				return NEGADO;
			} else if (e instanceof ValidacaoException){
				setActionErrors(Formatador.separaCritica(e.getMessage()));
				return ABRIR;
			} else {
				erro = true;
				erroMensagem = e.getMessage();
				return ERRO_SISTEMA;
			}
		}
    }

Agora mora o problema, no código abaixo, que está num EJB Stateless, o Stateful não funciona direito, criando sempre uma nova instancia.


	public void ativaAcao(Long idAcao) throws ValidacaoException, AutorizacaoException, PermissaoException {
		Permissao perm = getControleService().verificaPermissao("ACAO", "ATIVAR");
		List<String> problemas = new ArrayList<String>();
		
		Acao acao = obter(idAcao);
		
		if(perm.getTipo().equals(Permissao.Tipo.RESTRITA)){
			if(acao.getUsuarioCriador() == null || (acao.getUsuarioCriador() != null && acao.getUsuarioCriador().getIdentificador() != controleService.getUsuarioLogado().getIdentificador())){
				problemas.add("Não posso ativar uma Ação criada por outro usuário");
			}
		}

		if(acao.isAtivo()){
			problemas.add("Ação já esta ativa");
		}

		if(problemas.size() > 0){
			throw new ValidacaoException(Formatador.formaCritica(problemas));
		}

		acao.setAtivo(false);
		acaoRepository.save(acao);
	}

	public ControleService getControleService() throws PermissaoException {
		if(controleService == null){
			try {
				ServiceLocator serviceLocator = ServiceLocator.getInstance();
				controleService = (ControleService) serviceLocator.getLocalEJB(ControleService.class);
			} catch (Exception e) {
				throw (PermissaoException) e;
			}
		}
		
		return controleService;
	}

acima está o exemplo tentando usar o serviceLocator também no EJB Stateless. Estava fazendo com a anotação @EJB
mas também não funcionou.

Mas esse é o comportamento padrão. Usando um stateful a cada vez que você faz um lookup é retornando uma NOVA instância. O correto de um stateful é você armazenar em algum local e sempre referenciar a ele, e nunca fazer um segundo lookup.

o que esta me parecendo, é que quando faço a chamada ao serviceLocator na camada web ele enxerga a sessão e identifica que existe um Stateful ativo para aquela sessão, recuperando-o.
E quando está no EJB ele não enxerga que esse Stateful já existe.

Poderia passar o proxy por parâmetro da WEB para o EJB, mas teria que fazer em todos os métodos! Qual a forma correta de enxergar o EJB Stateful diretamente dentro de outro Stateless?

EJB Stateful injetado dentro de outro Stateful você consegue sim via @EJB normalmente.

Não te aconselho passar referência do módulo WEB para o módulo EJB, pois assim você viola a divisão entre as camadas. Até porque nem sempre você pode considerar que a instância que está na camada web pode ser acessada do módulo EJB e vice-versa. EJB tem esses pequenos detalhes a se pensar.

Me explica o que você realmente quer fazer, quem sabe assim dá para nós ter uma outra idéia?

Pessoal,

Alguém conseguiu achar uma solução para este problema?