Search in sources :

Example 1 with ReactiveTransaction

use of cn.taketoday.transaction.ReactiveTransaction in project today-infrastructure by TAKETODAY.

the class AbstractReactiveTransactionManager method getReactiveTransaction.

// ---------------------------------------------------------------------
// Implementation of ReactiveTransactionManager
// ---------------------------------------------------------------------
/**
 * This implementation handles propagation behavior. Delegates to
 * {@code doGetTransaction}, {@code isExistingTransaction}
 * and {@code doBegin}.
 *
 * @see #doGetTransaction
 * @see #isExistingTransaction
 * @see #doBegin
 */
@Override
public final Mono<ReactiveTransaction> getReactiveTransaction(@Nullable TransactionDefinition definition) throws TransactionException {
    // Use defaults if no transaction definition given.
    TransactionDefinition def = (definition != null ? definition : TransactionDefinition.withDefaults());
    return TransactionSynchronizationManager.forCurrentTransaction().flatMap(synchronizationManager -> {
        Object transaction = doGetTransaction(synchronizationManager);
        // Cache debug flag to avoid repeated checks.
        boolean debugEnabled = logger.isDebugEnabled();
        if (isExistingTransaction(transaction)) {
            // Existing transaction found -> check propagation behavior to find out how to behave.
            return handleExistingTransaction(synchronizationManager, def, transaction, debugEnabled);
        }
        // Check definition settings for new transaction.
        if (def.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
            return Mono.error(new InvalidTimeoutException("Invalid transaction timeout", def.getTimeout()));
        }
        // No existing transaction found -> check propagation behavior to find out how to proceed.
        if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
            return Mono.error(new IllegalTransactionStateException("No existing transaction found for transaction marked with propagation 'mandatory'"));
        } else if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED || def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW || def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
            return TransactionContextManager.currentContext().map(TransactionSynchronizationManager::new).flatMap(nestedSynchronizationManager -> suspend(nestedSynchronizationManager, null).map(Optional::of).defaultIfEmpty(Optional.empty()).flatMap(suspendedResources -> {
                if (debugEnabled) {
                    logger.debug("Creating new transaction with name [" + def.getName() + "]: " + def);
                }
                return Mono.defer(() -> {
                    GenericReactiveTransaction status = newReactiveTransaction(nestedSynchronizationManager, def, transaction, true, debugEnabled, suspendedResources.orElse(null));
                    return doBegin(nestedSynchronizationManager, transaction, def).doOnSuccess(ignore -> prepareSynchronization(nestedSynchronizationManager, status, def)).thenReturn(status);
                }).onErrorResume(ErrorPredicates.RUNTIME_OR_ERROR, ex -> resume(nestedSynchronizationManager, null, suspendedResources.orElse(null)).then(Mono.error(ex)));
            }));
        } else {
            // Create "empty" transaction: no actual transaction, but potentially synchronization.
            if (def.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT && logger.isWarnEnabled()) {
                logger.warn("Custom isolation level specified but no actual transaction initiated; " + "isolation level will effectively be ignored: " + def);
            }
            return Mono.just(prepareReactiveTransaction(synchronizationManager, def, null, true, debugEnabled, null));
        }
    });
}
Also used : TransactionDefinition(cn.taketoday.transaction.TransactionDefinition) Serial(java.io.Serial) ReactiveTransaction(cn.taketoday.transaction.ReactiveTransaction) TransactionDefinition(cn.taketoday.transaction.TransactionDefinition) InvalidTimeoutException(cn.taketoday.transaction.InvalidTimeoutException) Predicate(java.util.function.Predicate) ObjectInputStream(java.io.ObjectInputStream) ReactiveTransactionManager(cn.taketoday.transaction.ReactiveTransactionManager) AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) IOException(java.io.IOException) Mono(reactor.core.publisher.Mono) TransactionSuspensionNotSupportedException(cn.taketoday.transaction.TransactionSuspensionNotSupportedException) Serializable(java.io.Serializable) LoggerFactory(cn.taketoday.logging.LoggerFactory) Flux(reactor.core.publisher.Flux) List(java.util.List) Nullable(cn.taketoday.lang.Nullable) IllegalTransactionStateException(cn.taketoday.transaction.IllegalTransactionStateException) TransactionException(cn.taketoday.transaction.TransactionException) UnexpectedRollbackException(cn.taketoday.transaction.UnexpectedRollbackException) Logger(cn.taketoday.logging.Logger) Optional(java.util.Optional) InvalidTimeoutException(cn.taketoday.transaction.InvalidTimeoutException) Optional(java.util.Optional) IllegalTransactionStateException(cn.taketoday.transaction.IllegalTransactionStateException)

Example 2 with ReactiveTransaction

use of cn.taketoday.transaction.ReactiveTransaction in project today-infrastructure by TAKETODAY.

the class AbstractReactiveTransactionAspectTests method cannotCommitTransaction.

/**
 * Simulate failure of the underlying transaction infrastructure to commit.
 * Check that the target method was invoked, but that the transaction
 * infrastructure exception was thrown to the client
 */
@Test
public void cannotCommitTransaction() throws Exception {
    TransactionAttribute txatt = new DefaultTransactionAttribute();
    Method m = setNameMethod;
    MapTransactionAttributeSource tas = new MapTransactionAttributeSource();
    tas.register(m, txatt);
    // Method m2 = getNameMethod;
    // No attributes for m2
    ReactiveTransactionManager rtm = mock(ReactiveTransactionManager.class);
    ReactiveTransaction status = mock(ReactiveTransaction.class);
    given(rtm.getReactiveTransaction(txatt)).willReturn(Mono.just(status));
    UnexpectedRollbackException ex = new UnexpectedRollbackException("foobar", null);
    given(rtm.commit(status)).willReturn(Mono.error(ex));
    given(rtm.rollback(status)).willReturn(Mono.empty());
    DefaultTestBean tb = new DefaultTestBean();
    TestBean itb = (TestBean) advised(tb, rtm, tas);
    String name = "new name";
    Mono.from(itb.setName(name)).as(StepVerifier::create).consumeErrorWith(throwable -> {
        assertThat(throwable.getClass()).isEqualTo(RuntimeException.class);
        assertThat(throwable.getCause()).isEqualTo(ex);
    }).verify();
    // Should have invoked target and changed name
    itb.getName().as(StepVerifier::create).expectNext(name).verifyComplete();
}
Also used : BeforeEach(org.junit.jupiter.api.BeforeEach) ReactiveTransaction(cn.taketoday.transaction.ReactiveTransaction) StepVerifier(reactor.test.StepVerifier) Publisher(org.reactivestreams.Publisher) Assertions.assertThat(org.assertj.core.api.Assertions.assertThat) ReactiveTransactionManager(cn.taketoday.transaction.ReactiveTransactionManager) Mono(reactor.core.publisher.Mono) Mockito.times(org.mockito.Mockito.times) TransactionContext(cn.taketoday.transaction.reactive.TransactionContext) Fail.fail(org.assertj.core.api.Fail.fail) Mockito.verify(org.mockito.Mockito.verify) Mockito.verifyNoInteractions(org.mockito.Mockito.verifyNoInteractions) Test(org.junit.jupiter.api.Test) CannotCreateTransactionException(cn.taketoday.transaction.CannotCreateTransactionException) BDDMockito.given(org.mockito.BDDMockito.given) TransactionSystemException(cn.taketoday.transaction.TransactionSystemException) UnexpectedRollbackException(cn.taketoday.transaction.UnexpectedRollbackException) Method(java.lang.reflect.Method) Mockito.mock(org.mockito.Mockito.mock) Method(java.lang.reflect.Method) ReactiveTransaction(cn.taketoday.transaction.ReactiveTransaction) ReactiveTransactionManager(cn.taketoday.transaction.ReactiveTransactionManager) UnexpectedRollbackException(cn.taketoday.transaction.UnexpectedRollbackException) StepVerifier(reactor.test.StepVerifier) Test(org.junit.jupiter.api.Test)

Example 3 with ReactiveTransaction

use of cn.taketoday.transaction.ReactiveTransaction in project today-framework by TAKETODAY.

the class AbstractReactiveTransactionAspectTests method transactionShouldSucceedWithNotNew.

/**
 * Check that a transaction is created and committed.
 */
@Test
public void transactionShouldSucceedWithNotNew() throws Exception {
    TransactionAttribute txatt = new DefaultTransactionAttribute();
    MapTransactionAttributeSource tas = new MapTransactionAttributeSource();
    tas.register(getNameMethod, txatt);
    ReactiveTransaction status = mock(ReactiveTransaction.class);
    ReactiveTransactionManager rtm = mock(ReactiveTransactionManager.class);
    // expect a transaction
    given(rtm.getReactiveTransaction(txatt)).willReturn(Mono.just(status));
    given(rtm.commit(status)).willReturn(Mono.empty());
    DefaultTestBean tb = new DefaultTestBean();
    TestBean itb = (TestBean) advised(tb, rtm, tas);
    itb.getName().as(StepVerifier::create).verifyComplete();
    verify(rtm).commit(status);
}
Also used : ReactiveTransaction(cn.taketoday.transaction.ReactiveTransaction) ReactiveTransactionManager(cn.taketoday.transaction.ReactiveTransactionManager) Test(org.junit.jupiter.api.Test)

Example 4 with ReactiveTransaction

use of cn.taketoday.transaction.ReactiveTransaction in project today-framework by TAKETODAY.

the class AbstractReactiveTransactionManager method getReactiveTransaction.

// ---------------------------------------------------------------------
// Implementation of ReactiveTransactionManager
// ---------------------------------------------------------------------
/**
 * This implementation handles propagation behavior. Delegates to
 * {@code doGetTransaction}, {@code isExistingTransaction}
 * and {@code doBegin}.
 *
 * @see #doGetTransaction
 * @see #isExistingTransaction
 * @see #doBegin
 */
@Override
public final Mono<ReactiveTransaction> getReactiveTransaction(@Nullable TransactionDefinition definition) throws TransactionException {
    // Use defaults if no transaction definition given.
    TransactionDefinition def = (definition != null ? definition : TransactionDefinition.withDefaults());
    return TransactionSynchronizationManager.forCurrentTransaction().flatMap(synchronizationManager -> {
        Object transaction = doGetTransaction(synchronizationManager);
        // Cache debug flag to avoid repeated checks.
        boolean debugEnabled = logger.isDebugEnabled();
        if (isExistingTransaction(transaction)) {
            // Existing transaction found -> check propagation behavior to find out how to behave.
            return handleExistingTransaction(synchronizationManager, def, transaction, debugEnabled);
        }
        // Check definition settings for new transaction.
        if (def.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
            return Mono.error(new InvalidTimeoutException("Invalid transaction timeout", def.getTimeout()));
        }
        // No existing transaction found -> check propagation behavior to find out how to proceed.
        if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
            return Mono.error(new IllegalTransactionStateException("No existing transaction found for transaction marked with propagation 'mandatory'"));
        } else if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED || def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW || def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
            return TransactionContextManager.currentContext().map(TransactionSynchronizationManager::new).flatMap(nestedSynchronizationManager -> suspend(nestedSynchronizationManager, null).map(Optional::of).defaultIfEmpty(Optional.empty()).flatMap(suspendedResources -> {
                if (debugEnabled) {
                    logger.debug("Creating new transaction with name [" + def.getName() + "]: " + def);
                }
                return Mono.defer(() -> {
                    GenericReactiveTransaction status = newReactiveTransaction(nestedSynchronizationManager, def, transaction, true, debugEnabled, suspendedResources.orElse(null));
                    return doBegin(nestedSynchronizationManager, transaction, def).doOnSuccess(ignore -> prepareSynchronization(nestedSynchronizationManager, status, def)).thenReturn(status);
                }).onErrorResume(ErrorPredicates.RUNTIME_OR_ERROR, ex -> resume(nestedSynchronizationManager, null, suspendedResources.orElse(null)).then(Mono.error(ex)));
            }));
        } else {
            // Create "empty" transaction: no actual transaction, but potentially synchronization.
            if (def.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT && logger.isWarnEnabled()) {
                logger.warn("Custom isolation level specified but no actual transaction initiated; " + "isolation level will effectively be ignored: " + def);
            }
            return Mono.just(prepareReactiveTransaction(synchronizationManager, def, null, true, debugEnabled, null));
        }
    });
}
Also used : TransactionDefinition(cn.taketoday.transaction.TransactionDefinition) Serial(java.io.Serial) ReactiveTransaction(cn.taketoday.transaction.ReactiveTransaction) TransactionDefinition(cn.taketoday.transaction.TransactionDefinition) InvalidTimeoutException(cn.taketoday.transaction.InvalidTimeoutException) Predicate(java.util.function.Predicate) ObjectInputStream(java.io.ObjectInputStream) ReactiveTransactionManager(cn.taketoday.transaction.ReactiveTransactionManager) AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) IOException(java.io.IOException) Mono(reactor.core.publisher.Mono) TransactionSuspensionNotSupportedException(cn.taketoday.transaction.TransactionSuspensionNotSupportedException) Serializable(java.io.Serializable) LoggerFactory(cn.taketoday.logging.LoggerFactory) Flux(reactor.core.publisher.Flux) List(java.util.List) Nullable(cn.taketoday.lang.Nullable) IllegalTransactionStateException(cn.taketoday.transaction.IllegalTransactionStateException) TransactionException(cn.taketoday.transaction.TransactionException) UnexpectedRollbackException(cn.taketoday.transaction.UnexpectedRollbackException) Logger(cn.taketoday.logging.Logger) Optional(java.util.Optional) InvalidTimeoutException(cn.taketoday.transaction.InvalidTimeoutException) Optional(java.util.Optional) IllegalTransactionStateException(cn.taketoday.transaction.IllegalTransactionStateException)

Example 5 with ReactiveTransaction

use of cn.taketoday.transaction.ReactiveTransaction in project today-framework by TAKETODAY.

the class AbstractReactiveTransactionAspectTests method twoTransactionsShouldSucceed.

/**
 * Check that two transactions are created and committed.
 */
@Test
public void twoTransactionsShouldSucceed() throws Exception {
    TransactionAttribute txatt = new DefaultTransactionAttribute();
    MapTransactionAttributeSource tas1 = new MapTransactionAttributeSource();
    tas1.register(getNameMethod, txatt);
    MapTransactionAttributeSource tas2 = new MapTransactionAttributeSource();
    tas2.register(setNameMethod, txatt);
    ReactiveTransaction status = mock(ReactiveTransaction.class);
    ReactiveTransactionManager rtm = mock(ReactiveTransactionManager.class);
    // expect a transaction
    given(rtm.getReactiveTransaction(txatt)).willReturn(Mono.just(status));
    given(rtm.commit(status)).willReturn(Mono.empty());
    DefaultTestBean tb = new DefaultTestBean();
    TestBean itb = (TestBean) advised(tb, rtm, new TransactionAttributeSource[] { tas1, tas2 });
    itb.getName().as(StepVerifier::create).verifyComplete();
    Mono.from(itb.setName("myName")).as(StepVerifier::create).verifyComplete();
    verify(rtm, times(2)).commit(status);
}
Also used : ReactiveTransaction(cn.taketoday.transaction.ReactiveTransaction) ReactiveTransactionManager(cn.taketoday.transaction.ReactiveTransactionManager) Test(org.junit.jupiter.api.Test)

Aggregations

ReactiveTransaction (cn.taketoday.transaction.ReactiveTransaction)12 ReactiveTransactionManager (cn.taketoday.transaction.ReactiveTransactionManager)12 Test (org.junit.jupiter.api.Test)10 UnexpectedRollbackException (cn.taketoday.transaction.UnexpectedRollbackException)6 Mono (reactor.core.publisher.Mono)6 CannotCreateTransactionException (cn.taketoday.transaction.CannotCreateTransactionException)4 TransactionSystemException (cn.taketoday.transaction.TransactionSystemException)4 TransactionContext (cn.taketoday.transaction.reactive.TransactionContext)4 Method (java.lang.reflect.Method)4 Assertions.assertThat (org.assertj.core.api.Assertions.assertThat)4 Fail.fail (org.assertj.core.api.Fail.fail)4 BeforeEach (org.junit.jupiter.api.BeforeEach)4 BDDMockito.given (org.mockito.BDDMockito.given)4 Mockito.mock (org.mockito.Mockito.mock)4 Mockito.times (org.mockito.Mockito.times)4 Mockito.verify (org.mockito.Mockito.verify)4 Mockito.verifyNoInteractions (org.mockito.Mockito.verifyNoInteractions)4 Publisher (org.reactivestreams.Publisher)4 StepVerifier (reactor.test.StepVerifier)4 Nullable (cn.taketoday.lang.Nullable)2