Padrões e timeouts de JTA em Java EE
Seu sistema pode funcionar perfeitamente por anos, mas um dia, timeouts da JTA (Java Transaction API) começam a aparecer:
[WebLogic] Transaction Rolledback.: weblogic.transaction.internal.TimedOutException: Transaction timed out after 301 seconds
(...) No further JDBC access is allowed within this transaction....
[WebSphere] TimeoutManage I WTRN0006W: Transaction (...) has timed out after 120 seconds.
[JBoss/WildFly] javax.transaction.RollbackException: ARJUNA016102: The transaction is not active!
Embora específico para alguns casos de uso, a ocorrência de um timeout de transação JTA não é tão incomum e pode acontecer em produção. Especialmente, quando as quantidades de dados aumentam drasticamente em comparação com os limites definidos inicialmente. Talvez você também tenha acabado de mudar para um servidor de aplicação diferente?
No Spring, isso é moleza. Em nível de classe/interface/método, podemos configurar isso usando a anotação @org.springframework.transaction.annotation.Transactional(timeout = SECONDS)
. É o mesmo, independentemente de querermos usar a JTA fornecida pelo contêiner, uma embutida ou apenas um TM de fonte de dados simples.
No mundo Java EE, no entanto, isso não é tão direto. A anotação equivalente javax.transaction.Transactional
no momento não expõe uma API para configurar o timeout; essa melhoria foi deixada em aberto. Assim, o timeout padrão, bem como a capacidade de configurá-lo por unidade de trabalho, foram deixados para a decisão dos implementadores.
Exemplo e padrões
Vamos ver como isso funciona no ambiente Java EE. Definiremos um Singleton Bean simples que será instanciado na inicialização da aplicação:
package mypackage;
import javax.annotation.PostConstruct;
import javax.ejb.Singleton;
import javax.ejb.Startup;
@Startup
@Singleton(name = "StartupBean")
public class StartupBean {
@PostConstruct
public void init() {
System.out.println("Starting the initialization");
try {
Thread.sleep(31_000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Finished initializing");
}
}
Para ser mais explícito, este bean está faltando uma anotação relevante, que é @TransactionManagement(TransactionManagementType.CONTAINER)
. Isso pode não ser intuitivo para pessoas que estão começando com Java EE, mas:
Se esta anotação não for usada, assume-se que o bean tem gerenciamento de transação gerenciado pelo contêiner.
Além disso, no nível do método, também está faltando a anotação @TransactionAttribute(TransactionAttributeType.REQUIRED)
:
Se a anotação TransactionAttribute não for especificada, e o bean usar demarcação de transação gerenciada pelo contêiner, as semânticas do atributo de transação REQUIRED são assumidas.
Vale a pena lembrar desses padrões ao criar novos beans. No final, quando eu implanto isso em um WebLogic 12.2, recebo:
weblogic.ejb.container.InternalException: Transaction marked rollback or not expected transaction status: 4
e o WebLogic não consegue implantar a aplicação (ela permanece no status STATE_NEW). Isso ocorre porque o método @PostConstruct
também é coberto pela transação JTA, que, neste caso, tem um timeout padrão de 30 segundos.
Para desabilitar a JTA, podemos usar @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
em nível de método/classe ou a anotação @TransactionManagement(TransactionManagementType.BEAN)
em nível de classe.
Timeouts
Por outro lado, se realmente tivermos uma lógica transacional em tal método (provavelmente não no @PostConstruct
), provavelmente estaríamos interessados em configurar o timeout, assim como no Spring. Nesse caso, temos que consultar a documentação da implementação específica do contêiner JEE.
No caso do WebLogic, podemos configurar os timeouts através do console do WebLogic em nível de domínio.

Isso adiciona algumas entradas ao arquivo config.xml
encontrado na pasta de configuração do seu diretório de instalação do domínio:
<!--...-->
</security-configuration>
<jta>
<timeout-seconds>31</timeout-seconds>
<abandon-timeout-seconds>86400</abandon-timeout-seconds>
<forget-heuristics>true</forget-heuristics>
<before-completion-iteration-limit>10</before-completion-iteration-limit>
<max-transactions>10000</max-transactions>
<max-unique-name-statistics>1000</max-unique-name-statistics>
<checkpoint-interval-seconds>300</checkpoint-interval-seconds>
<parallel-xa-enabled>true</parallel-xa-enabled>
<unregister-resource-grace-period>30</unregister-resource-grace-period>
<two-phase-enabled>true</two-phase-enabled>
<clusterwide-recovery-enabled>false</clusterwide-recovery-enabled>
<tightly-coupled-transactions-enabled>false</tightly-coupled-transactions-enabled>
<tlog-write-when-determiner-exists-enabled>false</tlog-write-when-determiner-exists-enabled>
</jta>
<!--...-->
Uma configuração mais específica (em nível de bean) pode ser feita através do descritor weblogic-ejb-jar.xml
colocado no diretório WEB-INF do WAR ou META-INF do JAR. A configuração é correspondida através do ejb-name
definido também pelo elemento name
da anotação do bean.
<?xml version = '1.0' encoding = 'UTF-8'?>
<weblogic-ejb-jar xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.bea.com/ns/weblogic/weblogic-ejb-jar http://www.bea.com/ns/weblogic/weblogic-ejb-jar/1.0/weblogic-ejb-jar.xsd"
xmlns="http://www.bea.com/ns/weblogic/weblogic-ejb-jar">
<weblogic-enterprise-bean>
<ejb-name>StartupBean</ejb-name>
<transaction-descriptor>
<trans-timeout-seconds>60</trans-timeout-seconds>
</transaction-descriptor>
</weblogic-enterprise-bean>
</weblogic-ejb-jar>
Para outros provedores, pode ser diferente. Uma verificação rápida no mais recente guia de instalação da Oracle Commerce Platform dá uma visão geral para JBoss, WebLogic e WebSphere. O JBoss/WildFly tem uma anotação adicional @org.jboss.ejb3.annotation.TransactionTimeout
semelhante à do Spring. Assim como o WebLogic, o JBoss/Wildfly também fornece um elemento de configuração <tx:trans-timeout>
no descritor jboss-ejb3.xml
. No WebSphere, por outro lado, este elemento é chamado de <global-transaction>
e é colocado no descritor ibm-ejb-jar-ext.xml
.
Finalmente, para um controle refinado, podemos mudar para BMT (Bean Managed Transactions). Nesse caso, usaríamos os métodos javax.transaction.TransactionManager.setTransactionTimeout(int seconds)
ou javax.transaction.UserTransaction.setTransactionTimeout(int seconds)
para definir o timeout.
Resumo
Existem alguns pontos-chave a serem lembrados:
- a API
@Transactional
difere entre Java EE e Spring; - os beans EJB têm CMT (Container Managed Transactions) habilitado por padrão, a menos que você declare explicitamente o contrário;
- controlar o timeout do CMT é específico da implementação; a melhor ideia é verificar a documentação do contêiner.