/*
* 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.
}
};
}