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:
| 1 2 3 4 | <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.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | <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”:
| 1 2 3 4 5 6 7 8 | <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”:
| 1 2 3 4 | <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 deUserTransaction 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 deUserTransaction.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | 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.
Autor: Dancovich