Change By: Hardy Ferentschik (07/Jun/12 5:38 AM)
Description:
In Hibernate 3.6.10, when creating JtaTransaction from JtaTransactionFactory  UserTransaction was always setted:
{code}
public Transaction createTransaction(JDBCContext jdbcContext, Context transactionContext) throws HibernateException {
    UserTransaction ut = getUserTransaction();
    return new JTATransaction( ut, jdbcContext, transactionContext );
}
{code}

This behavior changed in Hibernate 4.0.1 as UserTransaction will only get setted if explicitly beginning a new transaction ; UserTransaction is no longer passed by the JtaTransactionFactory:
{code}
protected void doBegin() {
    LOG.debug( "begin" );
    
    userTransaction = jtaPlatform().retrieveUserTransaction();
    if ( userTransaction == null ) {
        throw new TransactionException( "Unable to locate JTA UserTransaction" );
    }
    ...
}
{code}
This change of affect the JtaTransactionFactory::isJoinableJtaTransaction method behavior:
{code}
public boolean isJoinableJtaTransaction(TransactionCoordinator transactionCoordinator, JtaTransaction transaction) {
    try {
        // Essentially:
        // 1) If we have a local (Hibernate) transaction in progress
        //      and it already has the UserTransaction cached, use that
        //      UserTransaction to determine the status.
        // 2) If a transaction manager has been located, use
        //      that transaction manager to determine the status.
        // 3) Finally, as the last resort, try to lookup the
        //      UserTransaction via JNDI and use that to determine the
        //      status.
        if ( transaction != null ) {
            UserTransaction ut = transaction.getUserTransaction();
            if ( ut != null ) {
                return JtaStatusHelper.isActive( ut );
            }
        }
    ...
{code}
Hibernate 4.0.1: transaction.getUserTransaction() == null
Hibernate 3.6.10: transaction.getUserTransaction() != null

as stated in javadoc, if user transaction as not been set in 1), it will fallback on jtaPlatform.retrieveTransactionManager 2) and will later fail with "proxy handle is no longer valid" as UserTransaction as not been set.
see forum reference : https://forum.hibernate.org/viewtopic.php?f=1&t=1014848

As a workaround I've hacked the JtaTransaction::getUserTransaction() to:
{code}
    private static Field localStatus;
    @SuppressWarnings( {"UnusedDeclaration"})
    public UserTransaction getUserTransaction() {
        if (userTransaction != null){
            return userTransaction;
        }
        userTransaction = jtaPlatform().retrieveUserTransaction();
        if ( userTransaction == null ) {
            throw new TransactionException( "Unable to locate JTA UserTransaction" );
        }
        if (localStatus == null){
            try{
                localStatus = AbstractTransactionImpl.class.getDeclaredField("localStatus");
                localStatus.setAccessible(true);
            }
            catch (Exception e){
                throw new RuntimeException(e);
            }
        }
        try{
            localStatus.set(this, LocalStatus.ACTIVE);
        }
        catch (Exception e){
            throw new RuntimeException(e);
        }
        
        return userTransaction;
    }
{code}
which seem to resolve the issue.

Here's how Configuration and SessionFactory are created :
{code}
configuration = new Configuration();
configuration.setProperty("hibernate.connection.datasource", datasourceName);
// Hibernate Configuration Properties
configuration.setProperty("hibernate.dialect", "com.XXX.MySQL5InnoDBDialect");
configuration.setProperty("hibernate.show_sql", "false");
configuration.setProperty("hibernate.format_sql", "false");
configuration.setProperty("hibernate.max_fetch_depth", "0"); // recommended values between 0 and 3
configuration.setProperty("hibernate.default_batch_fetch_size", "16"); // recommended values 4, 8, 16
configuration.setProperty("hibernate.order_updates", "true");
configuration.setProperty("hibernate.generate_statistics", "true");
configuration.setProperty("hibernate.use_sql_comments", "false");
// Hibernate JDBC and Connection Properties
configuration.setProperty("hibernate.jdbc.batch_size", "30"); // recommended values between 5 and 30
configuration.setProperty("hibernate.jdbc.batch_versioned_data", "true");
configuration.setProperty("hibernate.connection.provider_class", "com.XXX.DatasourceConnectionProvider");
// Hibernate Cache Properties
configuration.setProperty("hibernate.cache.region.factory_class", "org.hibernate.cache.infinispan.InfinispanRegionFactory");
configuration.setProperty("hibernate.cache.use_minimal_puts", "true");
configuration.setProperty("hibernate.cache.use_query_cache", "false"); // true to activate L2 cache
configuration.setProperty("hibernate.cache.use_second_level_cache", "false"); // true to activate L2 cache
configuration.setProperty("hibernate.transaction.factory_class", "org.hibernate.transaction.JTATransactionFactory");
configuration.setProperty("hibernate.transaction.manager_lookup_class", "org.hibernate.transaction.JBossTransactionManagerLookup");
configuration.setProperty("jta.UserTransaction", JBossAppServerJtaPlatform.UT_NAME);
configuration.setProperty("hibernate.transaction.jta.platform", "org.hibernate.service.jta.platform.internal.JBossAppServerJtaPlatform");
// Miscellaneous Properties
configuration.setProperty("hibernate.current_session_context_class", "jta");
// deactivate hibernate envers
configuration.setProperty("hibernate.listeners.envers.autoRegister", "false");

BootstrapServiceRegistry bootstrapServiceRegistry = new BootstrapServiceRegistryBuilder().withApplicationClassLoader(this.getClass().getClassLoader()).build();
ServiceRegistry serviceRegistry = new ServiceRegistryBuilder(bootstrapServiceRegistry).applySettings(configuration.getProperties()).buildServiceRegistry();
SessionFactory sessionFactory = (SessionFactory) configuration.buildSessionFactory(serviceRegistry);
{code}

Is there any reason why UserTransaction is not injected anymore at creation time ? Is my [ugly] workaround valid ?

Thanks,
This message is automatically generated by JIRA.
If you think it was sent incorrectly, please contact your JIRA administrators.
For more information on JIRA, see: http://www.atlassian.com/software/jira