/**
* Copyright (c) 2013-2016, The SeedStack authors <http://seedstack.org>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.seedstack.seed.core.internal.transaction;
import org.aopalliance.intercept.MethodInvocation;
import org.seedstack.seed.SeedException;
import org.seedstack.seed.transaction.Propagation;
import org.seedstack.seed.transaction.spi.TransactionHandler;
import org.seedstack.seed.transaction.spi.TransactionMetadata;
/**
* This transaction manager implements local transactions behavior, i.e. transactions that cannot span on multiple
* resources.
*/
public class LocalTransactionManager extends AbstractTransactionManager {
@Override
protected Object doMethodInterception(TransactionLogger transactionLogger, MethodInvocation invocation, TransactionMetadata transactionMetadata, TransactionHandler<Object> transactionHandler) throws Throwable {
Object currentTransaction = transactionHandler.getCurrentTransaction();
PropagationResult propagationResult = handlePropagation(transactionMetadata.getPropagation(), currentTransaction);
if (propagationResult.isNewTransactionNeeded()) {
transactionLogger.log("initializing transaction handler");
transactionHandler.doInitialize(transactionMetadata);
}
Object result = null;
try {
if (propagationResult.isNewTransactionNeeded()) {
transactionLogger.log("creating a new transaction");
currentTransaction = transactionHandler.doCreateTransaction();
} else {
transactionLogger.log("participating in an existing transaction");
}
try {
if (propagationResult.isNewTransactionNeeded()) {
transactionLogger.log("beginning the transaction");
transactionHandler.doBeginTransaction(currentTransaction);
}
try {
result = doInvocation(transactionLogger, invocation, transactionMetadata, currentTransaction);
} catch (Throwable throwable) {
if (propagationResult.isNewTransactionNeeded()) {
transactionLogger.log("rolling back the transaction after invocation exception");
transactionHandler.doRollbackTransaction(currentTransaction);
} else if (transactionMetadata.isRollbackOnParticipationFailure()) {
transactionLogger.log("marking the transaction as rollback-only after invocation exception");
transactionHandler.doMarkTransactionAsRollbackOnly(currentTransaction);
}
throw throwable;
}
if (propagationResult.isNewTransactionNeeded()) {
transactionLogger.log("committing transaction");
transactionHandler.doCommitTransaction(currentTransaction);
}
} finally {
if (propagationResult.isNewTransactionNeeded()) {
transactionLogger.log("releasing transaction");
transactionHandler.doReleaseTransaction(currentTransaction);
}
}
} finally {
if (propagationResult.isNewTransactionNeeded()) {
transactionLogger.log("cleaning up transaction handler");
transactionHandler.doCleanup();
}
}
return result;
}
private PropagationResult handlePropagation(Propagation propagation, Object currentTransaction) {
switch (propagation) {
case MANDATORY:
if (currentTransaction == null) {
throw SeedException.createNew(TransactionErrorCode.TRANSACTION_NEEDED_WHEN_USING_PROPAGATION_MANDATORY);
}
return new PropagationResult(false);
case NEVER:
if (currentTransaction != null) {
throw SeedException.createNew(TransactionErrorCode.NO_TRANSACTION_ALLOWED_WHEN_USING_PROPAGATION_NEVER);
}
return new PropagationResult(false);
case NOT_SUPPORTED:
if (currentTransaction != null) {
throw SeedException.createNew(TransactionErrorCode.TRANSACTION_SUSPENSION_IS_NOT_SUPPORTED);
}
return new PropagationResult(false);
case REQUIRED:
return new PropagationResult(currentTransaction == null);
case REQUIRES_NEW:
return new PropagationResult(true);
case SUPPORTS:
return new PropagationResult(false);
default:
throw SeedException.createNew(TransactionErrorCode.PROPAGATION_NOT_SUPPORTED).put("propagation", propagation);
}
}
}