Alterações realizadas no EJB não permanecem fora do EJB

0 down

Eu criei EJB para efetuar alguns testes e aprimorar meus conhecimentos a estrutura no momento está a seguinte.

Tenho um EJB chamado ClienteEJB, e a sua implementação chamada ClienteBean Tenho uma Action chamada ClienteAction.

Dentro da ClienteAction eu faço uma chamada a um método qualquer do EJB passando como parametro uma instância de Cliente.

Dentro desse método do EJB eu realizo algumas modificações nos atributos, por exemplo, altero o nome do cliente.

Após o método EJB ser processado, o processamento volta para minha Action, só que neste momento todas as alterações realizadas no EJB não estão lá. A minha instância de Cliente continua exatamente com os mesmos atributos que tinha antes de entrar no método EJB.

Porquê isso acontece?, Como posso contornar isso? Isso é de extrema importância

Código EJB:

[code]package br.com.ejb;

import javax.ejb.Remote;
import javax.ejb.Stateless;

import br.com.entity.PreCadastroCliente;
import br.com.pine.ejb.dao.PineEntityDAO;

@Stateless(mappedName = “PreCadastroClienteEJB”)
@Remote(value = PreCadastroClienteEJB.class)
public class PreCadastroClienteBean extends PineEntityDAO implements PreCadastroClienteEJB {

public void foo(PreCadastroCliente pcc) {
	pcc.setNmFantasia("PCC");
}

}

[/code]

[code]
package br.com.ejb.struts;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;

import br.com.ejb.PreCadastroClienteEJB;
import br.com.entity.PreCadastroCliente;
import br.com.pine.ejb.PineServiceLocator;
import br.com.pine.struts.PineAction;

public class PreCadastroClienteAction extends PineAction {

public ActionForward cadPreCadastroCliente(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
	PreCadastroClienteEJB preCadastroClienteEJB = (PreCadastroClienteEJB) PineServiceLocator.findEJB(PreCadastroClienteEJB.class);
	PreCadastroCliente cliente = new PreCadastroCliente();
	cliente.setNmFantasia("Nome fantasia");
	preCadastroClienteEJB.foo(cliente);
	System.out.println("Nm fantasia após ejb: " + cliente.getNmFantasia());
	return mapping.findForward("cadCliente");
}[/code]

O objetivo dessa dúvida é que eu realizo inserts dentro do meu DAO, mas quem é responsável por chamar meus DAO é o EJB, lá dentro com JPA, eu insiro a nova entidade no banco de dados, a JPA cria um novo código sequencial pra essa entidade pra mim, e eu preciso que este código esteja disponível na minha Action.

Ninguém tem idéia do que pode estar acontecendo?

Testei o mesmo código no sistema da empresa e funcionou corretamente, a diferença é:

JBoss de lá:
4.0
EJB 2.1

JBoss meu
5.1
EJB 3.0

Fora isso não há nenhuma diferença, talvez alguma configuração, gostaria de saber porquê isso está ocorrendo só no meu projeto.

Olá,

Não sei se essa área é a mais apropriada do fórum para postar dúvidas sobre EJB. Talvez “Persistência: Hibernate, JPA, JDBC e outros” ou “Java Enterprise Edition (Java EE)”.

Mas, uma coisa me chamou atenção: você disse que testou o mesmo código em outro ambiente, porém esse ambiente além da versão anterior do JBoss, roda EJB 2.1. Então, você re-escreveu o EJB para usar XML no lugar de anotações? Não entendi isso.

Se for o caso, o link a seguir tem um mapeamento entre a configuração XML e anotações: EJB 3.0 annotations and their deployment descriptor equivalents

Algumas coisas que podem ajudar a encontrar o problema:

  • Qual configuração do JBoss 5 (all, default, minimal) você está usando?
  • A anotação @Remote não deveria estar na interface?
  • No log no JBoss 5 aparece algo sobre o deploy do seu EJB? Outra forma de confirmar isso é olhando na JNDI Tree View.
  • Como você faz o deploy? São dois projetos, um war para o cliente e um jar para o ejb?

Não não,

Eu quis dizer que eu usei a mesma idéia de código em outro ambiente, ja existiam EJB’s criados e Actions criadas, eu apenas fiz uma mudança no atributo de um objeto dentro do EJB e verifiquei se ela iria se refletir fora do EJB também.

Sobre a anotação @Remote na interface, eu dei uma olhada nesse tutorial aqui:
http://docs.jboss.org/ejb3/docs/tutorial/1.0.6/html_single/index.html#EJB3_Entities

Tem um exemplo utilizando a anotação @Remote no Bean que implementa o EJB.

Fora que, eu consigo fazer o lookup normalmente, o EJB funciona “normalmente” executa rolback das operações da JPA quando uma exceção não verificada é lançada etc, tudo certinho, só essa questão ai do objeto que não funciona.

Sobre a configuração do JBoss 5 não sei responder, como posso saber?
Em relação ao deploy do EJB, não tenho certeza se aparece no LOG, mas caso ele não tivesse gerado o deploy corretamente eu não conseguiria debugar o código dentro do EJB correto?

Quando eu faço o deploy eu apenas seleciono o arquivo .ear, automaticamente ele coloca todos os EJB e o WEB dentro do JBoss.

Vou verificar na JNDI Treeview como você disse, mas acredito que não faz sentido ele não ter sido deployado sendo que o lookup funciona normalmente

ja tentou assim??

por favor me corrigir se eu estiver errado… mas um EJB stateless e um bean sem estado… então ele serve para ser um serviço… não mantem estado das variaveis…
você deve retornar o objeto que foi processado assim.


@Stateless(mappedName = "PreCadastroClienteEJB")  
@Remote(value = PreCadastroClienteEJB.class)  
public class PreCadastroClienteBean extends PineEntityDAO<PreCadastroCliente> implements PreCadastroClienteEJB {  
  
    public PreCadastroCliente foo(PreCadastroCliente pcc) {  
        pcc.setNmFantasia("PCC");  

       return pcc;
    }  
} 

        cliente.setNmFantasia("Nome fantasia");  
        cliente =  preCadastroClienteEJB.foo(cliente);  
        System.out.println("Nm fantasia após ejb: " + cliente.getNmFantasia());  
        return mapping.findForward("cadCliente");

não é a melhor solução… mas deve resolver o problema.

[quote=daniesouza]ja tentou assim??

por favor me corrigir se eu estiver errado… mas um EJB stateless e um bean sem estado… então ele serve para ser um serviço… não mantem estado das variaveis…
você deve retornar o objeto que foi processado assim.


@Stateless(mappedName = "PreCadastroClienteEJB")  
@Remote(value = PreCadastroClienteEJB.class)  
public class PreCadastroClienteBean extends PineEntityDAO<PreCadastroCliente> implements PreCadastroClienteEJB {  
  
    public PreCadastroCliente foo(PreCadastroCliente pcc) {  
        pcc.setNmFantasia("PCC");  

       return pcc;
    }  
} 

        cliente.setNmFantasia("Nome fantasia");  
        cliente =  preCadastroClienteEJB.foo(cliente);  
        System.out.println("Nm fantasia após ejb: " + cliente.getNmFantasia());  
        return mapping.findForward("cadCliente");

não é a melhor solução… mas deve resolver o problema.[/quote]

Então cara, no ambiente que testei os EJB também são Stateless e funcionou, eu entendo que os EJB Stateless não mantém estado. Mas isso pelo que eu vi serve pra caso eu queira acessar o EJB novamente após executar outras operações, nesse caso está tudo no mesmo EJB.

Alguém tem mais alguma dica?

Isso é problema de configuração mesmo? A partir de qual JBoss o EJB 3.1 é suportado? Gostaria de testar em outro JBoss

Fala cara!

Lembre-se que a chamada remota não é feita por referência… Uma cópia do objeto é passado. Então na prática, o objeto que existe lá dentro do EJB não é o mesmo que você ta verificando o nome não. Por isso você não percebe essa alteração.

Quanto as versões do JBoss que suportam EJB 3.1, tenho certeza que o 7.x suporta, pois ele é completamente compatível com JEE 6. O JBoss 6 talvez também suporte, uma vez que ele foi feito para suportar parcialmente a especificação JEE 6, mas não lembro se ele suporta EJB 3.1. De qualquer maneira eu não recomendaria você usar o JBoss 6 pra nada. Usa o 7 mesmo.

Pra ficar mais claro, você tem que fazer isso aqui:

PreCadastroCliente novoObjeto = preCadastroClienteEJB.foo(cliente); //o metodo tem que retornar o cliente alterado System.out.println("Nm fantasia após ejb: " + novoObjeto.getNmFantasia());

[quote=rodrigo.uchoa]Pra ficar mais claro, você tem que fazer isso aqui:

PreCadastroCliente novoObjeto = preCadastroClienteEJB.foo(cliente); //o metodo tem que retornar o cliente alterado System.out.println("Nm fantasia após ejb: " + novoObjeto.getNmFantasia()); [/quote]

Sim eu entendo que essa é uma solução mas não acho ela elegante, estou sendo teimoso porque realizei o teste em outro ambiente e funcionou como o que eu esperava.

Tem alguma forma de fazer isso de forma transparente sem precisar retornar um objeto?

Nas configurações do arquivo JPA eu tive que especificar alguma configuração pro controle de transação funcionar, que eu passava uma classe do JBoss no parametro desse xml, Acho que é pra dizer que quem vai gerenciar a transação é o JBoss, acho que isso pode fazer parte do problema.

Vou tentar criar um novo projeto EJB 3.0 limpo, só pra executar esse teste e verificar.

Sobre o JBoss 7, com certeza vou usa-lo sim!

Cara, se for chamada remota você não tem escolha. Tem que retornar o objeto da forma que eu falei, pois não existe passagem por referência em chamadas remotas. O seu ejb tava anotado com @Remote, então estou imaginando que o que você quer é uma chamada remota. Não existe nenhuma configuração a mudar, você simplesmente está fazendo algo conceitualmente errado.

Se funcionou em algumas circunstâncias, é porque você deve esta usando um ejb remoto mas em chamadas locais, então acredito que vá depender de como o servidor trata esse caso. Mas também está no mínimo conceitualmente errado você usar um ejb remoto para fazer chamadas locais.

Pra finalizar, mesmo levando em consideração que você use um ejb com @Local, a forma mais elegante de fazer é sim retornando o objeto, ainda que seja uma referência para o mesmo, e não da forma como você quer fazer.

Abs.

[quote=rodrigo.uchoa]Cara, se for chamada remota você não tem escolha. Tem que retornar o objeto da forma que eu falei, pois não existe passagem por referência em chamadas remotas. O seu ejb tava anotado com @Remote, então estou imaginando que o que você quer é uma chamada remota. Não existe nenhuma configuração a mudar, você simplesmente está fazendo algo conceitualmente errado.

Se funcionou em algumas circunstâncias, é porque você deve esta usando um ejb remoto mas em chamadas locais, então acredito que vá depender de como o servidor trata esse caso. Mas também está no mínimo conceitualmente errado você usar um ejb remoto para fazer chamadas locais.

Pra finalizar, mesmo levando em consideração que você use um ejb com @Local, a forma mais elegante de fazer é sim retornando o objeto, ainda que seja uma referência para o mesmo, e não da forma como você quer fazer.

Abs.[/quote]

Como seria feito o lookup Local?, como ficaria as anotações se fossem locais?

Eu gostaria de entender como é possível utilizar ejb remotos em chamadas locais. Onde mudam as configurações, não que eu queira aplicar, entendi sua explicação e percebo no fundo que é a maneira correta, mas gostaria de aprender da outra forma também.

Digamos que eu tenha um método save que é persistido com JPA, de acordo com sua explicação devo retornar a entidade após o salvamento dentro do método save?

Ideal é não utilizar um ejb remoto para chamadas locais. Se for chamada local, use um local. Sem entrar em detalhes, basta anotar com @Local e não com @Remote. Para buscar uma referência basta usar qualquer mecanismo de inserção de dependência. @EJB, @Inject, @Autowired… Hoje as opções são muitas.

Exatamente. Retorne o objeto. Seja a chamada local ou remota.

Claro que se a arquitetura que você pretende criar é mesmo distrbuída, com EJB’s remotos, existem outras preocupações que vão bem além de simplesmente anotar o seu EJB com @Remote. Primeiro tenta aprender só considerando chamadas locais mesmo, depois você complica com chamadas remotas.

Ideal é não utilizar um ejb remoto para chamadas locais. Se for chamada local, use um local. Sem entrar em detalhes, basta anotar com @Local e não com @Remote. Para buscar uma referência basta usar qualquer mecanismo de inserção de dependência. @EJB, @Inject, @Autowired… Hoje as opções são muitas.

Exatamente. Retorne o objeto. Seja a chamada local ou remota.

Claro que se a arquitetura que você pretende criar é mesmo distrbuída, com EJB’s remotos, existem outras preocupações que vão bem além de simplesmente anotar o seu EJB com @Remote. Primeiro tenta aprender só considerando chamadas locais mesmo, depois você complica com chamadas remotas.[/quote]

AS idéias que você mostrou são todas anotações eu gostaria de criar um método que me retornasse a instância do EJB sem eu precisar anota-lo, com EJB Remoto tem como fazer isso pelo lookup e o cast, mas e pelo Local? não tem como fazer isso?

Claro. Tem como sim. O lookup é feito da mesma forma praticamente. É que para EJB’s locais o mais comum seria simplesmente usar DI. Mas se você tem um bom motivo, pode sim usar o velho e bom lookup.

VOu tentar, lembro de ter tentado realizar com o Local e recebia sempre a mensagem de EJB Not Found