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