HHH-3358 : non-JNDI distributed transaction support
git-svn-id: https://svn.jboss.org/repos/hibernate/core/trunk@14895 1b8cb986-b30d-0410-93ca-fae66ebed9b2
This commit is contained in:
parent
6db1fbcec5
commit
89ed8071d4
|
@ -23,8 +23,6 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.transaction;
|
package org.hibernate.transaction;
|
||||||
|
|
||||||
import javax.naming.InitialContext;
|
|
||||||
import javax.naming.NamingException;
|
|
||||||
import javax.transaction.Status;
|
import javax.transaction.Status;
|
||||||
import javax.transaction.Synchronization;
|
import javax.transaction.Synchronization;
|
||||||
import javax.transaction.SystemException;
|
import javax.transaction.SystemException;
|
||||||
|
@ -33,7 +31,6 @@ import javax.transaction.UserTransaction;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.hibernate.AssertionFailure;
|
|
||||||
import org.hibernate.HibernateException;
|
import org.hibernate.HibernateException;
|
||||||
import org.hibernate.Transaction;
|
import org.hibernate.Transaction;
|
||||||
import org.hibernate.TransactionException;
|
import org.hibernate.TransactionException;
|
||||||
|
@ -45,17 +42,10 @@ import org.hibernate.util.JTAHelper;
|
||||||
* a JTA {@link UserTransaction}. Similar to {@link CMTTransaction}, except
|
* a JTA {@link UserTransaction}. Similar to {@link CMTTransaction}, except
|
||||||
* here we are actually managing the transactions through the Hibernate
|
* here we are actually managing the transactions through the Hibernate
|
||||||
* transaction mechanism.
|
* transaction mechanism.
|
||||||
* <p/>
|
|
||||||
* Instances check to see if there is an existing JTA transaction. If none
|
|
||||||
* exists, a new transaction is started; if one exists, all work is done in the
|
|
||||||
* existing context. The following properties are used to locate the underlying
|
|
||||||
* {@link UserTransaction}:<ul>
|
|
||||||
* <li><tt>hibernate.jndi.url</tt> : JNDI initial context URL</li>
|
|
||||||
* <li><tt>hibernate.jndi.class</tt> : JNDI provider class</li>
|
|
||||||
* <li><tt>jta.UserTransaction</tt> : JNDI namespace</li>
|
|
||||||
* </ul>
|
|
||||||
*
|
*
|
||||||
* @author Gavin King
|
* @author Gavin King
|
||||||
|
* @author Steve Ebersole
|
||||||
|
* @author Les Hazlewood
|
||||||
*/
|
*/
|
||||||
public class JTATransaction implements Transaction {
|
public class JTATransaction implements Transaction {
|
||||||
|
|
||||||
|
@ -64,7 +54,7 @@ public class JTATransaction implements Transaction {
|
||||||
private final JDBCContext jdbcContext;
|
private final JDBCContext jdbcContext;
|
||||||
private final TransactionFactory.Context transactionContext;
|
private final TransactionFactory.Context transactionContext;
|
||||||
|
|
||||||
private UserTransaction ut;
|
private UserTransaction userTransaction;
|
||||||
private boolean newTransaction;
|
private boolean newTransaction;
|
||||||
private boolean begun;
|
private boolean begun;
|
||||||
private boolean commitFailed;
|
private boolean commitFailed;
|
||||||
|
@ -72,28 +62,12 @@ public class JTATransaction implements Transaction {
|
||||||
private boolean callback;
|
private boolean callback;
|
||||||
|
|
||||||
public JTATransaction(
|
public JTATransaction(
|
||||||
InitialContext context,
|
UserTransaction userTransaction,
|
||||||
String utName,
|
|
||||||
JDBCContext jdbcContext,
|
JDBCContext jdbcContext,
|
||||||
TransactionFactory.Context transactionContext
|
TransactionFactory.Context transactionContext) {
|
||||||
) {
|
|
||||||
this.jdbcContext = jdbcContext;
|
this.jdbcContext = jdbcContext;
|
||||||
this.transactionContext = transactionContext;
|
this.transactionContext = transactionContext;
|
||||||
|
this.userTransaction = userTransaction;
|
||||||
log.debug("Looking for UserTransaction under: " + utName);
|
|
||||||
|
|
||||||
try {
|
|
||||||
ut = (UserTransaction) context.lookup(utName);
|
|
||||||
}
|
|
||||||
catch (NamingException ne) {
|
|
||||||
log.error("Could not find UserTransaction in JNDI", ne);
|
|
||||||
throw new TransactionException("Could not find UserTransaction in JNDI: ", ne);
|
|
||||||
}
|
|
||||||
if (ut==null) {
|
|
||||||
throw new AssertionFailure("A naming service lookup returned null");
|
|
||||||
}
|
|
||||||
|
|
||||||
log.debug("Obtained UserTransaction");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -110,9 +84,9 @@ public class JTATransaction implements Transaction {
|
||||||
log.debug("begin");
|
log.debug("begin");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
newTransaction = ut.getStatus() == Status.STATUS_NO_TRANSACTION;
|
newTransaction = userTransaction.getStatus() == Status.STATUS_NO_TRANSACTION;
|
||||||
if (newTransaction) {
|
if (newTransaction) {
|
||||||
ut.begin();
|
userTransaction.begin();
|
||||||
log.debug("Began a new JTA transaction");
|
log.debug("Began a new JTA transaction");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -175,7 +149,7 @@ public class JTATransaction implements Transaction {
|
||||||
|
|
||||||
if (newTransaction) {
|
if (newTransaction) {
|
||||||
try {
|
try {
|
||||||
ut.commit();
|
userTransaction.commit();
|
||||||
commitSucceeded = true;
|
commitSucceeded = true;
|
||||||
log.debug("Committed JTA UserTransaction");
|
log.debug("Committed JTA UserTransaction");
|
||||||
}
|
}
|
||||||
|
@ -223,12 +197,12 @@ public class JTATransaction implements Transaction {
|
||||||
try {
|
try {
|
||||||
if (newTransaction) {
|
if (newTransaction) {
|
||||||
if (!commitFailed) {
|
if (!commitFailed) {
|
||||||
ut.rollback();
|
userTransaction.rollback();
|
||||||
log.debug("Rolled back JTA UserTransaction");
|
log.debug("Rolled back JTA UserTransaction");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
ut.setRollbackOnly();
|
userTransaction.setRollbackOnly();
|
||||||
log.debug("set JTA UserTransaction to rollback only");
|
log.debug("set JTA UserTransaction to rollback only");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -254,7 +228,7 @@ public class JTATransaction implements Transaction {
|
||||||
}
|
}
|
||||||
int status=NULL;
|
int status=NULL;
|
||||||
try {
|
try {
|
||||||
status = ut.getStatus();
|
status = userTransaction.getStatus();
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
log.error("Could not determine transaction status after commit", e);
|
log.error("Could not determine transaction status after commit", e);
|
||||||
|
@ -275,13 +249,9 @@ public class JTATransaction implements Transaction {
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public boolean wasRolledBack() throws TransactionException {
|
public boolean wasRolledBack() throws TransactionException {
|
||||||
|
|
||||||
//if (!begun) return false;
|
|
||||||
//if (commitFailed) return true;
|
|
||||||
|
|
||||||
final int status;
|
final int status;
|
||||||
try {
|
try {
|
||||||
status = ut.getStatus();
|
status = userTransaction.getStatus();
|
||||||
}
|
}
|
||||||
catch (SystemException se) {
|
catch (SystemException se) {
|
||||||
log.error("Could not determine transaction status", se);
|
log.error("Could not determine transaction status", se);
|
||||||
|
@ -299,12 +269,9 @@ public class JTATransaction implements Transaction {
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public boolean wasCommitted() throws TransactionException {
|
public boolean wasCommitted() throws TransactionException {
|
||||||
|
|
||||||
//if (!begun || commitFailed) return false;
|
|
||||||
|
|
||||||
final int status;
|
final int status;
|
||||||
try {
|
try {
|
||||||
status = ut.getStatus();
|
status = userTransaction.getStatus();
|
||||||
}
|
}
|
||||||
catch (SystemException se) {
|
catch (SystemException se) {
|
||||||
log.error("Could not determine transaction status", se);
|
log.error("Could not determine transaction status", se);
|
||||||
|
@ -327,7 +294,7 @@ public class JTATransaction implements Transaction {
|
||||||
|
|
||||||
final int status;
|
final int status;
|
||||||
try {
|
try {
|
||||||
status = ut.getStatus();
|
status = userTransaction.getStatus();
|
||||||
}
|
}
|
||||||
catch (SystemException se) {
|
catch (SystemException se) {
|
||||||
log.error("Could not determine transaction status", se);
|
log.error("Could not determine transaction status", se);
|
||||||
|
@ -381,7 +348,7 @@ public class JTATransaction implements Transaction {
|
||||||
*/
|
*/
|
||||||
public void setTimeout(int seconds) {
|
public void setTimeout(int seconds) {
|
||||||
try {
|
try {
|
||||||
ut.setTransactionTimeout(seconds);
|
userTransaction.setTransactionTimeout(seconds);
|
||||||
}
|
}
|
||||||
catch (SystemException se) {
|
catch (SystemException se) {
|
||||||
throw new TransactionException("could not set transaction timeout", se);
|
throw new TransactionException("could not set transaction timeout", se);
|
||||||
|
@ -394,6 +361,6 @@ public class JTATransaction implements Transaction {
|
||||||
* @return Value for property 'userTransaction'.
|
* @return Value for property 'userTransaction'.
|
||||||
*/
|
*/
|
||||||
protected UserTransaction getUserTransaction() {
|
protected UserTransaction getUserTransaction() {
|
||||||
return ut;
|
return userTransaction;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,57 +44,177 @@ import org.hibernate.util.JTAHelper;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Factory for {@link JTATransaction} instances.
|
* Factory for {@link JTATransaction} instances.
|
||||||
|
* <p/>
|
||||||
|
* To be completely accurate to the JTA spec, JTA implementations should
|
||||||
|
* publish their contextual {@link UserTransaction} reference into JNDI.
|
||||||
|
* However, in practice there are quite a few <tt>stand-alone</tt>
|
||||||
|
* implementations intended for use outside of J2EE/JEE containers and
|
||||||
|
* which therefore do not publish their {@link UserTransaction} references
|
||||||
|
* into JNDI but which otherwise follow the aspects of the JTA specification.
|
||||||
|
* This {@link TransactionFactory} implementation can support both models.
|
||||||
|
* <p/>
|
||||||
|
* For complete JTA implementations (including dependence on JNDI), the
|
||||||
|
* {@link UserTransaction} reference is obtained by a call to
|
||||||
|
* {@link #resolveInitialContext}. Hibernate will then attempt to locate the
|
||||||
|
* {@link UserTransaction} within this resolved
|
||||||
|
* {@link InitialContext} based on the namespace returned by
|
||||||
|
* {@link #resolveUserTransactionName}.
|
||||||
|
* <p/>
|
||||||
|
* For the so-called <tt>stand-alone</tt> implementations, we do not care at
|
||||||
|
* all about the JNDI aspects just described. Here, the implementation would
|
||||||
|
* have a specific manner to obtain a reference to its contextual
|
||||||
|
* {@link UserTransaction}; usually this would be a static code reference, but
|
||||||
|
* again it varies. Anyway, for each implementation the integration would need
|
||||||
|
* to override the {@link #getUserTransaction} method and return the appropriate
|
||||||
|
* thing.
|
||||||
*
|
*
|
||||||
* @author Gavin King
|
* @author Gavin King
|
||||||
|
* @author Steve Ebersole
|
||||||
|
* @author Les Hazlewood
|
||||||
*/
|
*/
|
||||||
public class JTATransactionFactory implements TransactionFactory {
|
public class JTATransactionFactory implements TransactionFactory {
|
||||||
|
public static final String DEFAULT_USER_TRANSACTION_NAME = "java:comp/UserTransaction";
|
||||||
private static final Logger log = LoggerFactory.getLogger( JTATransactionFactory.class );
|
private static final Logger log = LoggerFactory.getLogger( JTATransactionFactory.class );
|
||||||
private static final String DEFAULT_USER_TRANSACTION_NAME = "java:comp/UserTransaction";
|
|
||||||
|
|
||||||
protected InitialContext context;
|
protected InitialContext initialContext;
|
||||||
protected String utName;
|
protected String userTransactionName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configure this transaction factory. Specifically here we are attempting to
|
||||||
|
* resolve both an {@link #getInitialContext InitialContext} as well as the
|
||||||
|
* {@link #getUserTransactionName() JNDI namespace} for the {@link UserTransaction}.
|
||||||
|
*
|
||||||
|
* @param props The configuration properties
|
||||||
|
*
|
||||||
|
* @exception HibernateException
|
||||||
|
*/
|
||||||
public void configure(Properties props) throws HibernateException {
|
public void configure(Properties props) throws HibernateException {
|
||||||
|
this.initialContext = resolveInitialContext( props );
|
||||||
|
this.userTransactionName = resolveUserTransactionName( props );
|
||||||
|
log.trace( "Configured JTATransactionFactory to use [{}] for UserTransaction JDNI namespace", userTransactionName );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given the lot of Hibernate configuration properties, resolve appropriate
|
||||||
|
* reference to JNDI {@link InitialContext}.
|
||||||
|
* <p/>
|
||||||
|
* In general, the properties in which we are interested here all begin with
|
||||||
|
* <tt>hibernate.jndi</tt>. Especially important depending on your
|
||||||
|
* environment are {@link Environment#JNDI_URL hibernate.jndi.url} and
|
||||||
|
* {@link Environment#JNDI_CLASS hibernate.jndi.class}
|
||||||
|
*
|
||||||
|
* @param properties The Hibernate config properties.
|
||||||
|
* @return The resolved InitialContext.
|
||||||
|
*/
|
||||||
|
protected final InitialContext resolveInitialContext(Properties properties) {
|
||||||
try {
|
try {
|
||||||
context = NamingHelper.getInitialContext( props );
|
return NamingHelper.getInitialContext( properties );
|
||||||
}
|
}
|
||||||
catch ( NamingException ne ) {
|
catch ( NamingException ne ) {
|
||||||
log.error( "Could not obtain initial context", ne );
|
|
||||||
throw new HibernateException( "Could not obtain initial context", ne );
|
throw new HibernateException( "Could not obtain initial context", ne );
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
utName = props.getProperty( Environment.USER_TRANSACTION );
|
/**
|
||||||
|
* Given the lot of Hibernate configuration properties, resolve appropriate
|
||||||
|
* JNDI namespace to use for {@link UserTransaction} resolution.
|
||||||
|
* <p/>
|
||||||
|
* We determine the namespace to use by<ol>
|
||||||
|
* <li>Any specified {@link Environment#USER_TRANSACTION jta.UserTransaction} config property</li>
|
||||||
|
* <li>If a {@link TransactionManagerLookup} was indicated, use its
|
||||||
|
* {@link TransactionManagerLookup#getUserTransactionName}</li>
|
||||||
|
* <li>finally, as a last resort, we use {@link #DEFAULT_USER_TRANSACTION_NAME}</li>
|
||||||
|
* </ol>
|
||||||
|
*
|
||||||
|
* @param properties The Hibernate config properties.
|
||||||
|
* @return The resolved {@link UserTransaction} namespace
|
||||||
|
*/
|
||||||
|
protected final String resolveUserTransactionName(Properties properties) {
|
||||||
|
String utName = properties.getProperty( Environment.USER_TRANSACTION );
|
||||||
if ( utName == null ) {
|
if ( utName == null ) {
|
||||||
TransactionManagerLookup lookup = TransactionManagerLookupFactory.getTransactionManagerLookup( props );
|
TransactionManagerLookup lookup = TransactionManagerLookupFactory.getTransactionManagerLookup( properties );
|
||||||
if ( lookup != null ) {
|
if ( lookup != null ) {
|
||||||
utName = lookup.getUserTransactionName();
|
utName = lookup.getUserTransactionName();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return utName == null ? DEFAULT_USER_TRANSACTION_NAME : utName;
|
||||||
if ( utName == null ) {
|
|
||||||
utName = DEFAULT_USER_TRANSACTION_NAME;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
public Transaction createTransaction(JDBCContext jdbcContext, Context transactionContext)
|
public Transaction createTransaction(JDBCContext jdbcContext, Context transactionContext)
|
||||||
throws HibernateException {
|
throws HibernateException {
|
||||||
return new JTATransaction( context, utName, jdbcContext, transactionContext );
|
UserTransaction ut = getUserTransaction();
|
||||||
|
return new JTATransaction( ut, jdbcContext, transactionContext );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the {@link UserTransaction} reference.
|
||||||
|
*
|
||||||
|
* @return The appropriate {@link UserTransaction} reference.
|
||||||
|
*/
|
||||||
|
protected UserTransaction getUserTransaction() {
|
||||||
|
log.trace( "Attempting to locate UserTransaction via JNDI [{}]", getUserTransactionName() );
|
||||||
|
|
||||||
|
try {
|
||||||
|
UserTransaction ut = ( UserTransaction ) getInitialContext().lookup( getUserTransactionName() );
|
||||||
|
if ( ut == null ) {
|
||||||
|
throw new TransactionException( "Naming service lookup for UserTransaction returned null [" + getUserTransactionName() +"]" );
|
||||||
|
}
|
||||||
|
|
||||||
|
log.trace( "Obtained UserTransaction" );
|
||||||
|
|
||||||
|
return ut;
|
||||||
|
}
|
||||||
|
catch ( NamingException ne ) {
|
||||||
|
throw new TransactionException( "Could not find UserTransaction in JNDI [" + getUserTransaction() + "]", ne );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Getter for property 'initialContext'.
|
||||||
|
*
|
||||||
|
* @return Value for property 'initialContext'.
|
||||||
|
*/
|
||||||
|
protected InitialContext getInitialContext() {
|
||||||
|
return initialContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Getter for property 'userTransactionName'.
|
||||||
|
* The algorithm here is
|
||||||
|
*
|
||||||
|
* @return Value for property 'userTransactionName'.
|
||||||
|
*/
|
||||||
|
protected String getUserTransactionName() {
|
||||||
|
return userTransactionName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
public ConnectionReleaseMode getDefaultReleaseMode() {
|
public ConnectionReleaseMode getDefaultReleaseMode() {
|
||||||
return ConnectionReleaseMode.AFTER_STATEMENT;
|
return ConnectionReleaseMode.AFTER_STATEMENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
public boolean isTransactionManagerRequired() {
|
public boolean isTransactionManagerRequired() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
public boolean areCallbacksLocalToHibernateTransactions() {
|
public boolean areCallbacksLocalToHibernateTransactions() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
public boolean isTransactionInProgress(
|
public boolean isTransactionInProgress(
|
||||||
JDBCContext jdbcContext,
|
JDBCContext jdbcContext,
|
||||||
Context transactionContext,
|
Context transactionContext,
|
||||||
|
@ -120,14 +240,9 @@ public class JTATransactionFactory implements TransactionFactory {
|
||||||
return JTAHelper.isInProgress( jdbcContext.getFactory().getTransactionManager().getStatus() );
|
return JTAHelper.isInProgress( jdbcContext.getFactory().getTransactionManager().getStatus() );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
try {
|
UserTransaction ut = getUserTransaction();
|
||||||
UserTransaction ut = ( UserTransaction ) context.lookup( utName );
|
|
||||||
return ut != null && JTAHelper.isInProgress( ut.getStatus() );
|
return ut != null && JTAHelper.isInProgress( ut.getStatus() );
|
||||||
}
|
}
|
||||||
catch ( NamingException ne ) {
|
|
||||||
throw new TransactionException( "Unable to locate UserTransaction to check status", ne );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch ( SystemException se ) {
|
catch ( SystemException se ) {
|
||||||
throw new TransactionException( "Unable to check transaction status", se );
|
throw new TransactionException( "Unable to check transaction status", se );
|
||||||
|
|
Loading…
Reference in New Issue