HHH-5384 - HEM should not register its own Synchronization

git-svn-id: https://svn.jboss.org/repos/hibernate/core/trunk@19962 1b8cb986-b30d-0410-93ca-fae66ebed9b2
This commit is contained in:
Steve Ebersole 2010-07-16 19:04:26 +00:00
parent 207a95a49a
commit 1dd65401f3
12 changed files with 610 additions and 131 deletions

View File

@ -0,0 +1,41 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2010, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.engine.transaction;
import org.hibernate.HibernateException;
/**
* TODO : javadoc
*
* @author Steve Ebersole
*/
public class NullSynchronizationException extends HibernateException {
public NullSynchronizationException() {
this( "Synchronization to register cannot be null" );
}
public NullSynchronizationException(String s) {
super( s );
}
}

View File

@ -0,0 +1,101 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2010, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.engine.transaction;
import java.util.LinkedHashSet;
import javax.transaction.Synchronization;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.hibernate.HibernateException;
/**
* Manages a registry of {@link Synchronization Synchronizations}.
*
* @author Steve Ebersole
*/
public class SynchronizationRegistry {
private static final Logger log = LoggerFactory.getLogger( SynchronizationRegistry.class );
private LinkedHashSet<Synchronization> synchronizations;
/**
* Register a user {@link Synchronization} callback for this transaction.
*
* @param synchronization The synchronization callback to register.
*
* @throws HibernateException
*/
public void registerSynchronization(Synchronization synchronization) {
if ( synchronization == null ) {
throw new NullSynchronizationException();
}
if ( synchronizations == null ) {
synchronizations = new LinkedHashSet<Synchronization>();
}
boolean added = synchronizations.add( synchronization );
if ( !added ) {
log.info( "Synchronization [{}] was already registered", synchronization );
}
}
/**
* Delegate {@link Synchronization#beforeCompletion} calls to {@link #registerSynchronization registered}
* {@link Synchronization Synchronizations}
*/
public void notifySynchronizationsBeforeTransactionCompletion() {
if ( synchronizations != null ) {
for ( Synchronization synchronization : synchronizations ) {
try {
synchronization.beforeCompletion();
}
catch ( Throwable t ) {
log.error( "exception calling user Synchronization [{}]", synchronization, t );
}
}
}
}
/**
* Delegate {@link Synchronization#afterCompletion} calls to {@link #registerSynchronization registered}
* {@link Synchronization Synchronizations}
*
* @param status The transaction status (if known) per {@link javax.transaction.Status}
*/
public void notifySynchronizationsAfterTransactionCompletion(int status) {
if ( synchronizations != null ) {
for ( Synchronization synchronization : synchronizations ) {
try {
synchronization.afterCompletion( status );
}
catch ( Throwable t ) {
log.error( "exception calling user Synchronization [{}]", synchronization, t );
}
}
}
}
}

View File

@ -41,6 +41,8 @@ import org.hibernate.Interceptor;
import org.hibernate.SessionException;
import org.hibernate.Transaction;
import org.hibernate.TransactionException;
import org.hibernate.transaction.synchronization.CallbackCoordinator;
import org.hibernate.transaction.synchronization.HibernateSynchronizationImpl;
import org.hibernate.util.JTAHelper;
import org.hibernate.engine.SessionFactoryImplementor;
import org.hibernate.exception.JDBCExceptionHelper;
@ -82,6 +84,8 @@ public class JDBCContext implements Serializable, ConnectionManager.Callback {
private transient boolean isTransactionCallbackRegistered;
private transient Transaction hibernateTransaction;
private CallbackCoordinator jtaSynchronizationCallbackCoordinator;
public JDBCContext(Context owner, Connection connection, Interceptor interceptor) {
this.owner = owner;
this.connectionManager = new ConnectionManager(
@ -107,6 +111,20 @@ public class JDBCContext implements Serializable, ConnectionManager.Callback {
private JDBCContext() {
}
public CallbackCoordinator getJtaSynchronizationCallbackCoordinator() {
return jtaSynchronizationCallbackCoordinator;
}
public CallbackCoordinator getJtaSynchronizationCallbackCoordinator(javax.transaction.Transaction jtaTransaction) {
jtaSynchronizationCallbackCoordinator = new CallbackCoordinator( owner, this, jtaTransaction, hibernateTransaction );
return jtaSynchronizationCallbackCoordinator;
}
public void cleanUpJtaSynchronizationCallbackCoordinator() {
jtaSynchronizationCallbackCoordinator = null;
}
// ConnectionManager.Callback implementation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
public void connectionOpened() {
@ -194,7 +212,10 @@ public class JDBCContext implements Serializable, ConnectionManager.Callback {
if ( hibernateTransaction == null ) {
hibernateTransaction = owner.getFactory().getSettings().getTransactionFactory().createTransaction( this, owner );
}
tx.registerSynchronization( new CacheSynchronization(owner, this, tx, hibernateTransaction) );
tx.registerSynchronization(
new HibernateSynchronizationImpl( getJtaSynchronizationCallbackCoordinator( tx ) )
);
// tx.registerSynchronization( new CacheSynchronization(owner, this, tx, hibernateTransaction) );
isTransactionCallbackRegistered = true;
log.debug("successfully registered Synchronization");
return true;

View File

@ -1,10 +1,10 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
* Copyright (c) 2010, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Middleware LLC.
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
@ -20,13 +20,10 @@
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*
*/
package org.hibernate.transaction;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import javax.transaction.Status;
import javax.transaction.Synchronization;
@ -36,11 +33,11 @@ import org.slf4j.LoggerFactory;
import org.hibernate.HibernateException;
import org.hibernate.Transaction;
import org.hibernate.TransactionException;
import org.hibernate.engine.transaction.SynchronizationRegistry;
import org.hibernate.jdbc.JDBCContext;
/**
* {@link Transaction} implementation based on transaction management through
* a JDBC {@link java.sql.Connection}.
* {@link Transaction} implementation based on transaction management through a JDBC {@link java.sql.Connection}.
* <p/>
* This the Hibernate's default transaction strategy.
*
@ -48,9 +45,9 @@ import org.hibernate.jdbc.JDBCContext;
* @author Gavin King
*/
public class JDBCTransaction implements Transaction {
private static final Logger log = LoggerFactory.getLogger(JDBCTransaction.class);
private final SynchronizationRegistry synchronizationRegistry = new SynchronizationRegistry();
private final JDBCContext jdbcContext;
private final TransactionFactory.Context transactionContext;
@ -59,7 +56,6 @@ public class JDBCTransaction implements Transaction {
private boolean rolledBack;
private boolean committed;
private boolean commitFailed;
private List synchronizations;
private boolean callback;
private int timeout = -1;
@ -137,7 +133,7 @@ public class JDBCTransaction implements Transaction {
transactionContext.managedFlush(); //if an exception occurs during flush, user must call rollback()
}
notifyLocalSynchsBeforeTransactionCompletion();
notifySynchronizationsBeforeTransactionCompletion();
if ( callback ) {
jdbcContext.beforeTransactionCompletion( this );
}
@ -149,7 +145,7 @@ public class JDBCTransaction implements Transaction {
if ( callback ) {
jdbcContext.afterTransactionCompletion( true, this );
}
notifyLocalSynchsAfterTransactionCompletion( Status.STATUS_COMMITTED );
notifySynchronizationsAfterTransactionCompletion( Status.STATUS_COMMITTED );
}
catch (SQLException e) {
log.error("JDBC commit failed", e);
@ -157,7 +153,7 @@ public class JDBCTransaction implements Transaction {
if ( callback ) {
jdbcContext.afterTransactionCompletion( false, this );
}
notifyLocalSynchsAfterTransactionCompletion( Status.STATUS_UNKNOWN );
notifySynchronizationsAfterTransactionCompletion( Status.STATUS_UNKNOWN );
throw new TransactionException("JDBC commit failed", e);
}
finally {
@ -196,11 +192,11 @@ public class JDBCTransaction implements Transaction {
rollbackAndResetAutoCommit();
log.debug("rolled back JDBC Connection");
rolledBack = true;
notifyLocalSynchsAfterTransactionCompletion(Status.STATUS_ROLLEDBACK);
notifySynchronizationsAfterTransactionCompletion(Status.STATUS_ROLLEDBACK);
}
catch (SQLException e) {
log.error("JDBC rollback failed", e);
notifyLocalSynchsAfterTransactionCompletion(Status.STATUS_UNKNOWN);
notifySynchronizationsAfterTransactionCompletion(Status.STATUS_UNKNOWN);
throw new TransactionException("JDBC rollback failed", e);
}
finally {
@ -258,41 +254,17 @@ public class JDBCTransaction implements Transaction {
/**
* {@inheritDoc}
*/
public void registerSynchronization(Synchronization sync) throws HibernateException {
if (sync==null) throw new NullPointerException("null Synchronization");
if (synchronizations==null) {
synchronizations = new ArrayList();
}
synchronizations.add(sync);
public void registerSynchronization(Synchronization sync) {
synchronizationRegistry.registerSynchronization( sync );
}
private void notifyLocalSynchsBeforeTransactionCompletion() {
if (synchronizations!=null) {
for ( int i=0; i<synchronizations.size(); i++ ) {
Synchronization sync = (Synchronization) synchronizations.get(i);
try {
sync.beforeCompletion();
}
catch (Throwable t) {
log.error("exception calling user Synchronization", t);
}
}
}
private void notifySynchronizationsBeforeTransactionCompletion() {
synchronizationRegistry.notifySynchronizationsBeforeTransactionCompletion();
}
private void notifyLocalSynchsAfterTransactionCompletion(int status) {
private void notifySynchronizationsAfterTransactionCompletion(int status) {
begun = false;
if (synchronizations!=null) {
for ( int i=0; i<synchronizations.size(); i++ ) {
Synchronization sync = (Synchronization) synchronizations.get(i);
try {
sync.afterCompletion(status);
}
catch (Throwable t) {
log.error("exception calling user Synchronization", t);
}
}
}
synchronizationRegistry.notifySynchronizationsAfterTransactionCompletion( status );
}
/**

View File

@ -0,0 +1,35 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2010, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.transaction.synchronization;
import org.hibernate.transaction.TransactionFactory;
/**
* TODO : javadoc
*
* @author Steve Ebersole
*/
public interface AfterCompletionAction {
public void doAction(TransactionFactory.Context ctx, int status);
}

View File

@ -0,0 +1,51 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2010, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.transaction.synchronization;
import javax.transaction.SystemException;
import javax.transaction.Transaction;
import org.hibernate.transaction.TransactionFactory;
/**
* Contract for checking whether to perform a managed flush in a
* {@link javax.transaction.Synchronization#beforeCompletion()} callback
*
* @author Steve Ebersole
*/
public interface BeforeCompletionManagedFlushChecker {
/**
* Check whether we should perform the managed flush
*
* @param ctx The Hibernate "transaction context"
* @param jtaTransaction The JTA transaction
*
* @return True to indicate to perform the managed flush; false otherwise.
*
* @throws SystemException Can be thrown while accessing the JTA transaction; will result in transaction being
* marked for rollback (best effort).
*/
public boolean shouldDoManagedFlush(TransactionFactory.Context ctx, Transaction jtaTransaction)
throws SystemException;
}

View File

@ -0,0 +1,186 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2010, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.transaction.synchronization;
import javax.persistence.spi.PersistenceUnitTransactionType;
import javax.transaction.Status;
import javax.transaction.SystemException;
import javax.transaction.Transaction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.hibernate.HibernateException;
import org.hibernate.TransactionException;
import org.hibernate.jdbc.JDBCContext;
import org.hibernate.transaction.TransactionFactory;
import org.hibernate.util.JTAHelper;
/**
* Manages callbacks from the {@link javax.transaction.Synchronization} registered by Hibernate.
*
* @author Steve Ebersole
*/
public class CallbackCoordinator {
private static final Logger log = LoggerFactory.getLogger( CallbackCoordinator.class );
private final TransactionFactory.Context ctx;
private JDBCContext jdbcContext;
private final Transaction jtaTransaction;
private final org.hibernate.Transaction hibernateTransaction;
private BeforeCompletionManagedFlushChecker beforeCompletionManagedFlushChecker;
private AfterCompletionAction afterCompletionAction;
private ExceptionMapper exceptionMapper;
public CallbackCoordinator(
TransactionFactory.Context ctx,
JDBCContext jdbcContext,
Transaction jtaTransaction,
org.hibernate.Transaction hibernateTransaction) {
this.ctx = ctx;
this.jdbcContext = jdbcContext;
this.jtaTransaction = jtaTransaction;
this.hibernateTransaction = hibernateTransaction;
reset();
}
public void reset() {
beforeCompletionManagedFlushChecker = STANDARD_MANAGED_FLUSH_CHECKER;
exceptionMapper = STANDARD_EXCEPTION_MAPPER;
afterCompletionAction = STANDARD_AFTER_COMPLETION_ACTION;
}
public BeforeCompletionManagedFlushChecker getBeforeCompletionManagedFlushChecker() {
return beforeCompletionManagedFlushChecker;
}
public void setBeforeCompletionManagedFlushChecker(BeforeCompletionManagedFlushChecker beforeCompletionManagedFlushChecker) {
this.beforeCompletionManagedFlushChecker = beforeCompletionManagedFlushChecker;
}
public ExceptionMapper getExceptionMapper() {
return exceptionMapper;
}
public void setExceptionMapper(ExceptionMapper exceptionMapper) {
this.exceptionMapper = exceptionMapper;
}
public AfterCompletionAction getAfterCompletionAction() {
return afterCompletionAction;
}
public void setAfterCompletionAction(AfterCompletionAction afterCompletionAction) {
this.afterCompletionAction = afterCompletionAction;
}
// sync callbacks ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
public void beforeCompletion() {
log.trace( "transaction before completion callback" );
boolean flush;
try {
flush = beforeCompletionManagedFlushChecker.shouldDoManagedFlush( ctx, jtaTransaction );
}
catch ( SystemException se ) {
setRollbackOnly();
throw exceptionMapper.mapStatusCheckFailure( "could not determine transaction status in beforeCompletion()", se );
}
try {
if ( flush ) {
log.trace( "automatically flushing session" );
ctx.managedFlush();
}
}
catch ( RuntimeException re ) {
setRollbackOnly();
throw exceptionMapper.mapManagedFlushFailure( "error during managed flush", re );
}
finally {
jdbcContext.beforeTransactionCompletion( hibernateTransaction );
}
}
private void setRollbackOnly() {
try {
jtaTransaction.setRollbackOnly();
}
catch ( SystemException se ) {
// best effort
log.error( "could not set transaction to rollback only", se );
}
}
public void afterCompletion(int status) {
log.trace( "transaction after completion callback [status={}]", status );
try {
afterCompletionAction.doAction( ctx, status );
final boolean wasSuccessful = ( status == Status.STATUS_COMMITTED );
jdbcContext.afterTransactionCompletion( wasSuccessful, hibernateTransaction );
}
finally {
reset();
jdbcContext.cleanUpJtaSynchronizationCallbackCoordinator();
if ( ctx.shouldAutoClose() && !ctx.isClosed() ) {
log.trace( "automatically closing session" );
ctx.managedClose();
}
}
}
private static final BeforeCompletionManagedFlushChecker STANDARD_MANAGED_FLUSH_CHECKER = new BeforeCompletionManagedFlushChecker() {
public boolean shouldDoManagedFlush(TransactionFactory.Context ctx, Transaction jtaTransaction)
throws SystemException {
return !ctx.isFlushModeNever() &&
ctx.isFlushBeforeCompletionEnabled() &&
!JTAHelper.isRollback( jtaTransaction.getStatus() );
//actually, this last test is probably unnecessary, since
//beforeCompletion() doesn't get called during rollback
}
};
private static final ExceptionMapper STANDARD_EXCEPTION_MAPPER = new ExceptionMapper() {
public RuntimeException mapStatusCheckFailure(String message, SystemException systemException) {
log.error( "could not determine transaction status [{}]", systemException.getMessage() );
return new TransactionException( "could not determine transaction status in beforeCompletion()", systemException );
}
public RuntimeException mapManagedFlushFailure(String message, RuntimeException failure) {
log.error( "Error during managed flush [{}]", failure.getMessage() );
return failure;
}
};
private static final AfterCompletionAction STANDARD_AFTER_COMPLETION_ACTION = new AfterCompletionAction() {
public void doAction(TransactionFactory.Context ctx, int status) {
// nothing to do by default.
}
};
}

View File

@ -0,0 +1,53 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2010, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.transaction.synchronization;
import javax.transaction.SystemException;
/**
* TODO : javadoc
*
* @author Steve Ebersole
*/
public interface ExceptionMapper {
/**
* Map a JTA {@link SystemException} to the appropriate runtime-based exception.
*
* @param message The message to use for the returned exception
* @param systemException The causal exception
*
* @return The appropriate exception to throw
*/
public RuntimeException mapStatusCheckFailure(String message, SystemException systemException);
/**
* Map an exception encountered during a managed flush to the appropriate runtime-based exception.
*
* @param message The message to use for the returned exception
* @param failure The causal exception
*
* @return The appropriate exception to throw
*/
public RuntimeException mapManagedFlushFailure(String message, RuntimeException failure);
}

View File

@ -0,0 +1,61 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2010, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.transaction.synchronization;
import javax.transaction.Synchronization;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link Synchronization} implementation Hibernate registers with the JTA {@link javax.transaction.Transaction}
*
* @author Gavin King
* @author Steve Ebersole
*/
public class HibernateSynchronizationImpl implements Synchronization {
private static final Logger log = LoggerFactory.getLogger( HibernateSynchronizationImpl.class );
private final CallbackCoordinator coordinator;
public HibernateSynchronizationImpl(CallbackCoordinator coordinator) {
this.coordinator = coordinator;
}
/**
* {@inheritDoc}
*/
public void beforeCompletion() {
log.trace( "JTA sync : beforeCompletion()" );
coordinator.beforeCompletion();
}
/**
* {@inheritDoc}
*/
public void afterCompletion(int status) {
log.trace( "JTA sync : afterCompletion({})", status );
coordinator.afterCompletion( status );
}
}

View File

@ -101,6 +101,10 @@ import org.hibernate.engine.query.sql.NativeSQLQueryReturn;
import org.hibernate.engine.query.sql.NativeSQLQueryRootReturn;
import org.hibernate.proxy.HibernateProxy;
import org.hibernate.transaction.TransactionFactory;
import org.hibernate.transaction.synchronization.AfterCompletionAction;
import org.hibernate.transaction.synchronization.BeforeCompletionManagedFlushChecker;
import org.hibernate.transaction.synchronization.CallbackCoordinator;
import org.hibernate.transaction.synchronization.ExceptionMapper;
import org.hibernate.transform.BasicTransformerAdapter;
import org.hibernate.util.JTAHelper;
import org.hibernate.util.ReflectHelper;
@ -1017,73 +1021,31 @@ public abstract class AbstractEntityManagerImpl implements HibernateEntityManage
//flush before completion and
//register clear on rollback
log.trace( "Adding flush() and close() synchronization" );
joinableCMTTransaction.registerSynchronization(
new Synchronization() {
public void beforeCompletion() {
boolean flush = false;
TransactionFactory.Context ctx = null;
try {
ctx = ( TransactionFactory.Context ) session;
JoinableCMTTransaction joinable = ( JoinableCMTTransaction ) session.getTransaction();
javax.transaction.Transaction transaction = joinable.getTransaction();
if ( transaction == null ) {
log.warn(
"Transaction not available on beforeCompletionPhase: assuming valid"
);
}
flush = !ctx.isFlushModeNever() &&
//ctx.isFlushBeforeCompletionEnabled() &&
//TODO probably make it ! isFlushBeforecompletion()
( transaction == null || !JTAHelper.isRollback( transaction.getStatus() ) );
//transaction == null workaround a JBoss TMBug
}
catch ( SystemException se ) {
log.error( "could not determine transaction status", se );
PersistenceException pe = new PersistenceException(
"could not determine transaction status in beforeCompletion()",
se
);
// handlePersistenceException will mark the transaction as rollbacked
handlePersistenceException( pe );
throw pe;
}
catch ( HibernateException he ) {
throwPersistenceException( he );
}
try {
if ( flush ) {
log.trace( "automatically flushing session" );
ctx.managedFlush();
}
else {
log.trace( "skipping managed flushing" );
}
}
catch ( HibernateException he ) {
throw convert( he );
}
catch ( PersistenceException pe ) {
handlePersistenceException( pe );
throw pe;
}
catch ( RuntimeException re ) {
PersistenceException wrapped = new PersistenceException( re );
handlePersistenceException( wrapped );
throw wrapped;
CallbackCoordinator callbackCoordinator = ( (SessionImplementor ) getSession() ).getJDBCContext().getJtaSynchronizationCallbackCoordinator();
if ( callbackCoordinator == null ) {
throw new AssertionFailure( "Expecting CallbackCoordinator to be non-null" );
}
callbackCoordinator.setBeforeCompletionManagedFlushChecker(
new BeforeCompletionManagedFlushChecker() {
public boolean shouldDoManagedFlush(TransactionFactory.Context ctx, javax.transaction.Transaction jtaTransaction)
throws SystemException {
if ( transaction == null ) {
log.warn( "Transaction not available on beforeCompletion: assuming valid" );
}
return !ctx.isFlushModeNever()
&& ( jtaTransaction == null || !JTAHelper.isRollback( jtaTransaction.getStatus() ) );
}
public void afterCompletion(int status) {
}
);
callbackCoordinator.setAfterCompletionAction(
new AfterCompletionAction() {
public void doAction(TransactionFactory.Context ctx, int status) {
try {
if ( Status.STATUS_ROLLEDBACK == status
&& transactionType == PersistenceUnitTransactionType.JTA ) {
if ( session.isOpen() ) {
if ( !ctx.isClosed() ) {
if ( Status.STATUS_ROLLEDBACK == status
&& transactionType == PersistenceUnitTransactionType.JTA ) {
session.clear();
}
}
if ( session.isOpen() ) {
//only reset if the session is opened since you can't get the Transaction otherwise
JoinableCMTTransaction joinable = ( JoinableCMTTransaction ) session.getTransaction();
joinable.resetStatus();
}
@ -1094,6 +1056,23 @@ public abstract class AbstractEntityManagerImpl implements HibernateEntityManage
}
}
);
callbackCoordinator.setExceptionMapper(
new ExceptionMapper() {
public RuntimeException mapStatusCheckFailure(String message, SystemException systemException) {
throw new PersistenceException( message, systemException );
}
public RuntimeException mapManagedFlushFailure(String message, RuntimeException failure) {
if ( HibernateException.class.isInstance( failure ) ) {
throw convert( failure );
}
if ( PersistenceException.class.isInstance( failure ) ) {
throw failure;
}
throw new PersistenceException( message, failure );
}
}
);
}
else {
log.warn( "Cannot join transaction: do not override {}", Environment.TRANSACTION_STRATEGY );

View File

@ -1,10 +1,10 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
* Copyright (c) 2010, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Middleware LLC.
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
@ -37,12 +37,10 @@ import org.hibernate.envers.tools.Pair;
import org.hibernate.FlushMode;
import org.hibernate.Session;
import javax.transaction.Synchronization;
/**
* @author Adam Warski (adam at warski dot org)
*/
public class AuditProcess implements BeforeTransactionCompletionProcess, Synchronization {
public class AuditProcess implements BeforeTransactionCompletionProcess {
private final RevisionInfoGenerator revisionInfoGenerator;
private final SessionImplementor session;
@ -157,12 +155,4 @@ public class AuditProcess implements BeforeTransactionCompletionProcess, Synchro
session.flush();
}
}
// Synchronization methods
public void beforeCompletion() {
doBeforeTransactionCompletion(session);
}
public void afterCompletion(int status) { }
}

View File

@ -55,18 +55,7 @@ public class AuditProcessManager {
auditProcess = new AuditProcess(revisionInfoGenerator, session);
auditProcesses.put(transaction, auditProcess);
/*
* HHH-5315: the process must be both a BeforeTransactionCompletionProcess and a TX Synchronization.
*
* In a resource-local tx env, the process is called after the flush, and populates the audit tables.
* Also, any exceptions that occur during that are propagated (if a Synchronization was used, the exceptions
* would be eaten).
*
* In a JTA env, the before transaction completion is called before the flush, so not all changes are yet
* written. However, Synchronization-s do propagate exceptions, so they can be safely used.
*/
session.getActionQueue().registerProcess(auditProcess);
session.getTransaction().registerSynchronization(auditProcess);
session.getActionQueue().registerProcess(new AfterTransactionCompletionProcess() {
public void doAfterTransactionCompletion(boolean success, SessionImplementor session) {