Integração VRaptor e EJB3

11 respostas
thiagobaptista

Olá!

Primeiro queria agradecer ao Lucas, ao Garcia e a todos que me responderam no tópico “Migrando um sistema de PHP para VRaptor”!

Mas continuando… minha questão agora é que, no sistema que vou migrar, provavelmente vou implementar as regras de negócio em EJB3. O VRaptor se integra bem com essa tecnologia?

É possível ter um código como o abaixo:

@Resource
public class MeuControllerBoladao {
      
     @EJB
     private MeuEjbBoladao meuEjbBoladao;

     @Post @Path("/cadastrar")
     public void cadastrarCoisa(Coisa coisaDeNegocio) {
          meuEjbBoladao.cadastrar(coisaDeNegocio);
     }

}

Como ficaria a questão das injeções de dependência? Entrariam em conflito, as do contêiner EJB e a do Spring “nativo” do VRaptor?

11 Respostas

G

Não, não é possível fazer isso. @EJB só funciona para beans gerenciados pelo container, ou seja, servlets e backing beans. Resources do Vraptor são gerenciados por ele mesmo, então é como se um não soubesse do outro.

Eu uso um service locator para ter acesso aos EJBs. Abaixo te passo um exemplo de um para JEE6. Há algum tempo o Lavieri estava fazendo alguns testes integrando EJB via CDI, mas não sei como ficou esse assunto.

public final class ServiceLocator {

    private static final Logger logger = LoggerFactory.getLogger(ServiceLocator.class);

    private static final String EJB_INFRAESTRUCTURE_LOOKUP = "Não foi possível estabelecer comunicação com o módulo EJB.";

    // inicializa o contexto JDNI local
    private static final InitialContext ctx;

    static {
        try {
            ctx = new InitialContext();
        } catch (NamingException e) {
            throw new InfraestructureException(EJB_INFRAESTRUCTURE_LOOKUP);
        }
    }

    /**
     * Retorna uma instância do EJB a partir da sua interface remota/local.
     * 
     * @param <T> Tipo da interface remota.
     * @param clazz Interface remota para fazer a busca.
     * @return Instância do EJB remoto.
     */
    public static <T> T lookup(Class<T> clazz) {
        final StringBuilder name = new StringBuilder(60);
        name.append("java:global/cob/cob-ejb/");
        name.append(clazz.getSimpleName().replaceAll("Remote|Local", "Bean"));
        name.append("!");
        name.append(clazz.getName());

        try {
            logger.info("looking for EJB {} by {}", clazz, name);
            return clazz.cast(ctx.lookup(name.toString()));

        } catch (NamingException e) {
            throw new InfraestructureException(e);
        }
    }
}
G

Meus resources ficam assim:

@Resource
public class RateController {

    private final RateServiceRemote rateService = ServiceLocator.lookup(RateServiceRemote.class);
    private final CustomResult result;

    @Get
    @Path( { "/rate/", "/rate/page-{paging}/" })
    public void list(Paging paging) {
        result.onExceptionUse(UserSpaceController.class).dashboard();

        result.include("rateList", rateService.findAll(paging));
    }
}
thiagobaptista

Entendi… obrigado pelo esclarecimento, Garcia.

Algum desenvolvedor desse framework saberia me responder se existe algum projeto de implementar essa funcionalidade futuramente no VRaptor?

G

O que seria implementar essa funcionalidade? Você quer dizer fazer funcionar o @EJB nos controllers do Vraptor? Se sim isso não é possível porque apenas servlets e backing beans podem receber injeção de EJB, além disso só funciona para stateless e locais. EJBs remotos e statefull devem ser usados via lookup normal.

Eu sei que o Spring possui uma integração com EJB (somente Stateless Session Beans), onde ele pode fazer o lookup. O Lucas deve saber melhor sobre isso, se dá ou não para usar essa integração via Spring.

http://static.springsource.org/spring/docs/2.5.x/reference/ejb.html

Lucas_Cavalcanti

não sei se dá pra fazer o spring funcionar direto com isso… mas dá pra fazer usando ComponentFactories… o problema é que teria que fazer mais ou menos uma para cada Bean remoto…

se vocês conseguirem definir uma usabilidade boa, a gente pode fazer de outro jeito…

G

No Vraptor 2.6 eu tinha criado uma factory que sempre que o bean possuia Remote ou Local no final do nome (que é padrão dos nomes dos EJBs) eu fazia um ServiceLocator.lookup. Quando migrei para o 3 não tinha nada assim e deixei quieto, até porque não há um padrão para fazer o lookup na arvore até o JEE5. Cada appserver exporta os EJBs com bem entende.

No caso do JEE6 há uma padronização, porém é necessário saber se é um EJB local ou remoto, se está na mesma webapp ou não, se está em módulo EJB ou WAR, etc. Não sei se dá para fazer uma coisa muito genérica, por isso tenho preferido em cada aplicação minha usar um service locator independente.

Ainda não comecei os estudos em CDI, então não sei se dá para fazer algo encima dele para injetar os EJBs, nem que sejam apenas os que estão em um mesmo EAR/WAR.

thiagobaptista

Foi isso mesmo que eu quis dizer, Garcia. Obrigado pelo esclarecimento.

Mas e se eu tentar um "atalho", que seja criar uma camada de "Serviço" feita de POJOs, que seria uma Façade para os EJBs, funcionaria? Por exemplo, dado o seguinte POJO, e considerando que a aplicação rodará num JBoss[color=yellow]ta[/color] da vida:

public class MeuServiceBoladao {

	@EJB
	private MeuEjbBoladao meuEjbBoladao;  

	public void cadastrar(Coisa coisaDeNegocio) {  
		meuEjbBoladao.cadastrar(coisaDeNegocio);  
	}  

}

...eu poderia meter uma @Component nele e, no Controller do VRaptor referenciá-lo, como por exemplo assim:

@Resource  
public class MeuControllerBoladao {

	private MeuServiceBoladao service;  
  
	@Post  @Path("/cadastrar")
	public void cadastrarCoisa(Coisa coisaDeNegocio) {  
		service.cadastrar(coisaDeNegocio);  
	}  
  
}

Ou seja, seria possível "colocar em cadeia" as injeções do contêiner EJB (que fornece a instância do EJB para o POJO) e DEPOIS do VRaptor (que pegaria o POJO com a instância do EJB e o injetaria no Controller)?

Caso não... acredito que não haveria problema então em instanciar esse service no Controller sem injeção, com o bom e velho [color=blue]new[/color], certo?

G

Entendi.

Porém você apenas vai consegui injetar o MeuEjbBoladao se o MeuServiceBoladao for um EJB. Mas se ele for um EJB você não consegue injeta-lo no controller do Vraptor.

Se voce fizer um new está errado, porque EJB você nunca faz new, e sim pede uma instância ao container a partir da àrvore JNDI ou via Service Locator (que faz a mesma coisa).

thiagobaptista

Ué? Como assim? Mesmo que, nesse caso, o MeuServiceBoladao seja apenas um POJO? Até onde eu sei, qualquer bean “normal” consegue receber uma injeção de um EJB, bastando apenas criar um atributo do tipo desse EJB e anotá-la com @EJB.

Se não fosse possível essa integração via DI, eu daria new não no EJB, mas no service…

G

Não, um bean normal nunca consegue ter injetado um EJB. Apenas classes que são gerenciadas pelo container, no caso Servlets, Backing Beans e EJBs. Você não consegue fazer via new porque se você faz new a classe não será gerenciada, então não haverá injeção.

O mesmo caso ocorre no EJB, no Spring, e em qualquer framework de DI. No Spring, por exemplo, você não pode simplesmente fazer um new na classe e achar que será injetada as dependencias. Vocẽ precisa pedir uma instancia pelo ApplicationContext.getBean, que é praticamente a mesma coisa que você fazer lookup de um EJB com o service locator.

Um outro exemplo é um servlet. Você já viu um servlet ser acessado via new MeuServlet? Não, ele é gerenciado pelo próprio container. Quando você faz um new em uma classe você cria uma instância fora do application server.

Se você fizer um new no service, esse service não terá o @EJB injetado porque esse service não será gerenciado.

thiagobaptista

Entendi, Garcia. Realmente, o problema era que eu não tinha entendido o conceito completamente.

Bom… façamos do jeito mais difícil… hehehe

Obrigado pelas respostas!

Criado 14 de julho de 2010
Ultima resposta 14 de jul. de 2010
Respostas 11
Participantes 3