Olá a todos, meu nome é Danilo Viana, trabalho no Serpro-SSA e este é meu primeiro post contribuindo para o Demoiselle. No momento estou participando de um projeto utilizando Demoiselle 2, Hibernate e two-phase commit, ou seja, precisariamos sincronizar acesso a dois bancos de dados em uma única transação.
Surgiu a possibilidade de nosso projeto não poder utilizar JBoss 6, que possui suporte nativo a two-phase commit através da API XATransaction. Ao invés disso o projeto passaria a executar em Tomcat 6, sem o mesmo suporte nativo. Com o projeto já bastante adiantado e sem possibilidade de voltar atrás em nossa decisão de tecnologia, foi necessário configurar o Tomcat 6 para ter suporte ao Demoiselle 2 utilizando JTA e two-phase commit.
Suporte a JSF 2
O primeiro problema que encontramos foi a implementação da Expression Language utilizada pelo Tomcat 6. No JSF 2 as expressões em EL suportam chamada de métodos com parâmetros mas esse suporte não está presente na biblioteca do Tomcat. Testamos a versão utilizada no Glassfish (disponível no repositório Maven http://download.java.net/maven/glassfish sob o group-id “el-impl”) que funcionou sem problemas.
Baixe via Maven ou acessando o repositório no browser, os arquivos “el-api.jar” e “el-impl.jar”, acesse a pasta [TOMCAT 6]\lib e copie estes arquivos lá, substituindo o arquivo el-api.jar já existente (faça um backup do arquivo original).
Além disso é necessário ensinar o Tomcat a fabricar o objeto que processa instruções EL. Para isso no arquivo “web.xml” do seu projeto web adicione o parâmetro abaixo:
<context-param>
<param-name>com.sun.faces.expressionFactory</param-name>
<param-value>com.sun.el.ExpressionFactoryImpl</param-value>
</context-param>
Habilitando two-phase commit
O Tomcat não tem suporte nativo a api XATransaction, que provê suporte a two-phase commit. Para ter este suporte vamos utilizar o framework Atomikos (http://www.atomikos.com), que é livre e está sob licença Apache 2.0
O Atomikos está no repositório geral Maven, então basta acrescentar em seu projeto a dependência à biblioteca “transactions-hibernate3″, que está no group-id “com.atomikos” e seu projeto já poderá utilizar two-phase commit no JPA com Hibernate como persistence provider.
É necessário copiar em seu projeto uma classe que substitui o ObjectFactory original do Tomcat 6, do contrário seu projeto não será capaz de obter instâncias de UserTransaction já que o Tomcat 6 não tem suporte a fornecer objetos dessa classe. Acesse este link do site do Atomikos e copie a classe ou o jar disponibilizado para seu projeto.
Agora para configurar o acesso à conexão. Este é um exemplo de criação de recurso JNDI no arquivo “context.xml”, onde criamos tanto o recurso de acesso à conexões quanto o recurso de acesso à transações via UserTransaction. Note o parâmetro “factory”, que referencia a classe copiada no passo anterior. Note também que estamos utilizando o PostgreSQL como exemplo, pois esse SGBP suporta transações XA, verifique se seu SGBD oferece este suporte.
<Context>
<Resource
name="jdbc/nome_datasource"
auth="Container"
type="com.atomikos.jdbc.AtomikosDataSourceBean"
factory="com.atomikos.tomcat.BeanFactory"
uniqueResourceName="jdbc/nome_datasource"
xaDataSourceClassName="org.postgresql.xa.PGXADataSource"
xaProperties.databaseName="base_principal"
xaProperties.serverName="ip_servidor_banco_principal"
xaProperties.user="****"
xaProperties.password="****"
maxPoolSize="20"
/>
<Resource
name="UserTransaction" auth="Container"
type="javax.transaction.UserTransaction"
factory="com.atomikos.icatch.jta.UserTransactionFactory"
/>
</Context>
Utilizando JTA e Hibernate como persistence provider, é necessário incluir os seguintes parâmetros no arquivo “persistence.xml”:
<persistence-unit name="unidade_persistencia">
<jta-data-source>java:comp/env/jdbc/nome_datasource</jta-data-source>
<properties>
<!--...-->
<property name="hibernate.transaction.manager_lookup_class" value="com.atomikos.icatch.jta.hibernate3.TransactionManagerLookup" />
<!--...-->
</properties>
</persistence-unit>
Caso esteja utilizando Hibernate nativo, sem o JTA, estas configurações devem estar presentes em seu arquivo “hibernate.cfg.xml”:
<property name="hibernate.connection.datasource">java:comp/env/jdbc/nome_datasource</property>
<property name="hibernate.transaction.manager_lookup_class">com.atomikos.icatch.jta.hibernate3.TransactionManagerLookup</property>
<property name="hibernate.transaction.factory_class">org.hibernate.transaction.JTATransactionFactory</property>
<property name="jta.UserTransaction">java:comp/env/UserTransaction</property>
Por último, o Demoiselle 2 não está preparado para produzir instâncias de UserTransaction para Tomcat 6, então por ora é necessário acrescentar ao seu projeto um produtor de UserTransaction através da anotação @Produces.
Crie a classe abaixo em seu projeto, o pacote não é importante, mas note a anotação @Produces, ela vai indicar ao CDI que esta classe é responsável por produzir instâncias de UserTransaction.
package com.tomcat6.usertransactionsupport;
import javax.enterprise.inject.Produces;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.transaction.UserTransaction;
/**
* Classe responsável por produzir UserTransactions no Tomcat 6, pois
* o Demoiselle 2 não produz estas classes normalmente em um ambiente Tomcat.
*/
public class TomcatUserTransactionProducer {
@Produces
public UserTransaction create() {
UserTransaction transaction = null;
try {
Context context = new InitialContext();
transaction = (UserTransaction) context.lookup("java:comp/env/UserTransaction");
} catch (NamingException e) {
e.printStackTrace();
}
return transaction;
}
}
Conclusão
Com esses passos você será capaz de utilizar JSF 2 e two-phase commit no Tomcat 6. Se você está desenvolvendo um sistema com estes requisitos mas não pode utilizar um container com suporte nativo a estas funcionalidades, ou acredita que o JBoss é muito pesado e seu projeto simples não necessita de tanto, esta solução pode ser o que você procura.