CDI + JSF 2.0 e injeção do EntityManager com transação demarcada. Mini howto

Pessoal, conforme prometido, segue o código que permite injeção de dependência do EntityManager SEM Java EE no Tomcat 7 com CDI e JSF 2.0. Utilizei a implementação OWB (OpenWebBeans) da Apache, porque tive problemas com a implementação da JBoss (Weld) devido ao conflito da versão antiga do SL4J presente em Weld-servlet.jar que conflita com a versão 1.6 que preciso utilizar em minhas aplicações.

Lembram do OpenSessionInView? Pois bem, com essa solução ele não será mais necessário, uma vez que o entityManager produzido ficará no escopo de request e só será fechado pelo método que recebe o parâmetro anotado com @Disposes depois da fase RENDER_RESPONSE. Além disso, a transação só será aberta caso o método seja anotado com @RequerTransacao (obs: não é tão rica quantos os tipos de transação do Java EE).

Só há um problema na implementação que desconfio ser um bug na implementação da apache: o método que “disposes” o entityManager criado é evocado uma vez antes do método produtor ter sido acionado, resultando em um null pointer exception inofensivo, apesar disso, acho que vale apena ter o trabalho de postar a solução aqui.

Estou saindo de férias, então, só devo bater o olho no post depois do carnaval :slight_smile: Um bom carnaval para vocês!

Baixe OWB aqui http://www.apache.org/dyn/closer.cgi/openwebbeans/

Incluir as seguintes bibliotecas:
openwebbeans-impl-1.0.0.jar
openwebbeans-jsf-1.0.0.jar
openwebbeans-resource-1.0.0.jar
openwebbeans-spi-1.0.0.jar
openwebbeans-tomcat7-1.0.0.jar
openwebbeans-web-1.0.0.jar
scannotation-1.0.2.jar http://repository.jboss.org/maven2-brew/org/scannotation/scannotation/1.0.2/
geronimo-atinject_1.0_spec-1.0.jar
geronimo-interceptor_1.1_spec-1.0.jar http://tiny.cc/4exgy
geronimo-jcdi_1.0_spec-1.0.jar

Em web.xml, adicione:

<listener>
    <listener-class>org.apache.webbeans.servlet.WebBeansConfigurationListener</listener-class>
</listener>

Dentro de WEB-INF, criar o arquivo beans.xml. É neste arquivo que é necessário adicionar o interceptador que abrirá as transações.

<beans xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="
      http://java.sun.com/xml/ns/javaee
      http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
	<interceptors>
		<class>teste.RequerTransacaolInterceptor</class>
	</interceptors>
</beans>

Como a aplicação pode ter mais de uma unidade de persistência, é necessário criar um qualificador.

package teste;

import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.ElementType.PARAMETER;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import javax.inject.Qualifier;

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({ FIELD, METHOD, TYPE, PARAMETER })
public @interface EntityManagerBancoXQualifier {}

Agora, precisamos criar o bean que será o produtor do EntityManager da qualificação.

package teste;

import javax.enterprise.context.RequestScoped;
import javax.enterprise.inject.Disposes;
import javax.enterprise.inject.Produces;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

@RequestScoped
public class EntityManagerProducer {

	private @PersistenceContext(unitName = "BancoX")
	EntityManager entityManager;

	public EntityManagerProducer() {
	}

	@Produces
	@RequestScoped
	@EntityManagerBancoXQualifier
	public EntityManager createEntityManager() {
		System.out.println("EntityManager criado");
		return entityManager;
	}

	public void dispose(
			@Disposes @EntityManagerBancoXQualifier EntityManager entityManager) {
		System.out.println("EntityManager fechado");
		entityManager.close();
	}
}

É preciso criar um interceptador que será responsável pela demarcação da transação.

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import javax.interceptor.InterceptorBinding;

@InterceptorBinding
@Retention(RetentionPolicy.RUNTIME)
@Target( { ElementType.TYPE, ElementType.METHOD })
public @interface RequerTransacao
{

}
package teste;

import java.io.Serializable;

import javax.inject.Inject;
import javax.interceptor.AroundInvoke;
import javax.interceptor.Interceptor;
import javax.interceptor.InvocationContext;
import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;

@Interceptor
@RequerTransacao
public class RequerTransacaolInterceptor implements Serializable{
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private @Inject
	@EntityManagerBancoXQualifier
	EntityManager entityManager;

	@AroundInvoke
	public Object invoke(InvocationContext context) throws Exception {
		EntityTransaction transaction = entityManager.getTransaction();

		try {
			if (!transaction.isActive()) {
				transaction.begin();
			}

			return context.proceed();

		} catch (Exception e) {
			System.out.println("Chamando transação no método:" + e);
			if (transaction != null) {
				transaction.rollback();
			}

			throw e;

		} finally {
			if (transaction != null && transaction.isActive()) {
				transaction.commit();
			}
		}

	}
}

Pronto, agora, para poder usar o EntityManager em um CDI Bean, basta fazer o seguinte:

@Named
@RequestScoped
public class meuMB {

	@Inject @EntityManagerBancoXQualifier
	private EntityManager entityManager;
        (...)

       @RequerTransacao
	public String acaoQualquer() {
	      // use seu entityManager 
	}

Pronto. Eu ainda estou testando essa solução.

Muito legal a sua iniciativa.

Vou testar assim que possível.

Abraço.

Fala Flavio, tudo tranquilo?

Cara, me passou uma dúvida aqui que seria o seguinte: Eu estou tendo esse mesmo problema de injeção no EntityManager utilizando CDI com JSF 2 e o Tomcat 7. Diante dessa sua solução ai, que por sinal achei muito legal, gostaria de saber qual seria o problema se eu fizesse essa injeção do EntityManager na mão, algo do tipo:

public static final EntityManagerFactory emFactory = Persistence.createEntityManagerFactory(“cogerhPostgresDesenvolvimento”);
private EntityManager entityManager = emFactory.createEntityManager();

A estrutura do meu projeto está assim:

Tenho uma Interface DaoGenerico e a implementação do mesmo, onde nessa implementação tenho meu EntityManager, e tenho minhas interfaces BO e suas implementações, onde essas nessas implemantações dos BOs, eu deveria ter a interface do respectivo DAO, porém como não conseguia fazer a injeção de dependencia nele (antes conseguia fazer a injeção com o spring), então estou utilizando a implementação do DAO mesmo.

Abs.

Como sempre (rsrsrs), estou muito atarefado aqui na empresa, mas quero frisar o seguinte: este código depende de um plugin do Apache OpenWebBeans. Eu refatorei o código para trabalhar sem esse plugin, desta forma, deixando a solução mais portável (além de suportar transações de diversas unidades de persistência). A motivação para fazer isso é que encontrei alguns bugs na implementação da Apache.

Eu posto no GUJ assim que a solução estiver em produção.

Abraço

(rsrs) Sem querer cobrar, longe disto, mas vc já tem esta solução?

Obrigado

[quote=Flavio Almeida]Como sempre (rsrsrs), estou muito atarefado aqui na empresa, mas quero frisar o seguinte: este código depende de um plugin do Apache OpenWebBeans. Eu refatorei o código para trabalhar sem esse plugin, desta forma, deixando a solução mais portável (além de suportar transações de diversas unidades de persistência). A motivação para fazer isso é que encontrei alguns bugs na implementação da Apache.

Eu posto no GUJ assim que a solução estiver em produção.

Abraço[/quote]