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 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.