Search in sources :

Example 1 with CallbackPreferringPlatformTransactionManager

use of org.springframework.transaction.support.CallbackPreferringPlatformTransactionManager in project spring-framework by spring-projects.

the class TransactionAspectSupport method invokeWithinTransaction.

/**
 * General delegate for around-advice-based subclasses, delegating to several other template
 * methods on this class. Able to handle {@link CallbackPreferringPlatformTransactionManager}
 * as well as regular {@link PlatformTransactionManager} implementations and
 * {@link ReactiveTransactionManager} implementations for reactive return types.
 * @param method the Method being invoked
 * @param targetClass the target class that we're invoking the method on
 * @param invocation the callback to use for proceeding with the target invocation
 * @return the return value of the method, if any
 * @throws Throwable propagated from the target invocation
 */
@Nullable
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass, final InvocationCallback invocation) throws Throwable {
    // If the transaction attribute is null, the method is non-transactional.
    TransactionAttributeSource tas = getTransactionAttributeSource();
    final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
    final TransactionManager tm = determineTransactionManager(txAttr);
    if (this.reactiveAdapterRegistry != null && tm instanceof ReactiveTransactionManager) {
        boolean isSuspendingFunction = KotlinDetector.isSuspendingFunction(method);
        boolean hasSuspendingFlowReturnType = isSuspendingFunction && COROUTINES_FLOW_CLASS_NAME.equals(new MethodParameter(method, -1).getParameterType().getName());
        if (isSuspendingFunction && !(invocation instanceof CoroutinesInvocationCallback)) {
            throw new IllegalStateException("Coroutines invocation not supported: " + method);
        }
        CoroutinesInvocationCallback corInv = (isSuspendingFunction ? (CoroutinesInvocationCallback) invocation : null);
        ReactiveTransactionSupport txSupport = this.transactionSupportCache.computeIfAbsent(method, key -> {
            Class<?> reactiveType = (isSuspendingFunction ? (hasSuspendingFlowReturnType ? Flux.class : Mono.class) : method.getReturnType());
            ReactiveAdapter adapter = this.reactiveAdapterRegistry.getAdapter(reactiveType);
            if (adapter == null) {
                throw new IllegalStateException("Cannot apply reactive transaction to non-reactive return type: " + method.getReturnType());
            }
            return new ReactiveTransactionSupport(adapter);
        });
        InvocationCallback callback = invocation;
        if (corInv != null) {
            callback = () -> CoroutinesUtils.invokeSuspendingFunction(method, corInv.getTarget(), corInv.getArguments());
        }
        Object result = txSupport.invokeWithinTransaction(method, targetClass, callback, txAttr, (ReactiveTransactionManager) tm);
        if (corInv != null) {
            Publisher<?> pr = (Publisher<?>) result;
            return (hasSuspendingFlowReturnType ? KotlinDelegate.asFlow(pr) : KotlinDelegate.awaitSingleOrNull(pr, corInv.getContinuation()));
        }
        return result;
    }
    PlatformTransactionManager ptm = asPlatformTransactionManager(tm);
    final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
    if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {
        // Standard transaction demarcation with getTransaction and commit/rollback calls.
        TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);
        Object retVal;
        try {
            // This is an around advice: Invoke the next interceptor in the chain.
            // This will normally result in a target object being invoked.
            retVal = invocation.proceedWithInvocation();
        } catch (Throwable ex) {
            // target invocation exception
            completeTransactionAfterThrowing(txInfo, ex);
            throw ex;
        } finally {
            cleanupTransactionInfo(txInfo);
        }
        if (retVal != null && vavrPresent && VavrDelegate.isVavrTry(retVal)) {
            // Set rollback-only in case of Vavr failure matching our rollback rules...
            TransactionStatus status = txInfo.getTransactionStatus();
            if (status != null && txAttr != null) {
                retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status);
            }
        }
        commitTransactionAfterReturning(txInfo);
        return retVal;
    } else {
        Object result;
        final ThrowableHolder throwableHolder = new ThrowableHolder();
        // It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.
        try {
            result = ((CallbackPreferringPlatformTransactionManager) ptm).execute(txAttr, status -> {
                TransactionInfo txInfo = prepareTransactionInfo(ptm, txAttr, joinpointIdentification, status);
                try {
                    Object retVal = invocation.proceedWithInvocation();
                    if (retVal != null && vavrPresent && VavrDelegate.isVavrTry(retVal)) {
                        // Set rollback-only in case of Vavr failure matching our rollback rules...
                        retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status);
                    }
                    return retVal;
                } catch (Throwable ex) {
                    if (txAttr.rollbackOn(ex)) {
                        // A RuntimeException: will lead to a rollback.
                        if (ex instanceof RuntimeException) {
                            throw (RuntimeException) ex;
                        } else {
                            throw new ThrowableHolderException(ex);
                        }
                    } else {
                        // A normal return value: will lead to a commit.
                        throwableHolder.throwable = ex;
                        return null;
                    }
                } finally {
                    cleanupTransactionInfo(txInfo);
                }
            });
        } catch (ThrowableHolderException ex) {
            throw ex.getCause();
        } catch (TransactionSystemException ex2) {
            if (throwableHolder.throwable != null) {
                logger.error("Application exception overridden by commit exception", throwableHolder.throwable);
                ex2.initApplicationException(throwableHolder.throwable);
            }
            throw ex2;
        } catch (Throwable ex2) {
            if (throwableHolder.throwable != null) {
                logger.error("Application exception overridden by commit exception", throwableHolder.throwable);
            }
            throw ex2;
        }
        // Check result state: It might indicate a Throwable to rethrow.
        if (throwableHolder.throwable != null) {
            throw throwableHolder.throwable;
        }
        return result;
    }
}
Also used : Continuation(kotlin.coroutines.Continuation) AwaitKt(kotlinx.coroutines.reactive.AwaitKt) ReactiveTransaction(org.springframework.transaction.ReactiveTransaction) InitializingBean(org.springframework.beans.factory.InitializingBean) ConcurrentMap(java.util.concurrent.ConcurrentMap) ConcurrentReferenceHashMap(org.springframework.util.ConcurrentReferenceHashMap) KotlinDetector(org.springframework.core.KotlinDetector) TransactionManager(org.springframework.transaction.TransactionManager) TransactionSystemException(org.springframework.transaction.TransactionSystemException) BeanFactoryAware(org.springframework.beans.factory.BeanFactoryAware) MethodParameter(org.springframework.core.MethodParameter) Nullable(org.springframework.lang.Nullable) Method(java.lang.reflect.Method) BeanFactoryAnnotationUtils(org.springframework.beans.factory.annotation.BeanFactoryAnnotationUtils) ReactiveAdapterRegistry(org.springframework.core.ReactiveAdapterRegistry) ReactiveFlowKt(kotlinx.coroutines.reactive.ReactiveFlowKt) CoroutinesUtils(org.springframework.core.CoroutinesUtils) TransactionContextManager(org.springframework.transaction.reactive.TransactionContextManager) ReactiveAdapter(org.springframework.core.ReactiveAdapter) Properties(java.util.Properties) ClassUtils(org.springframework.util.ClassUtils) CallbackPreferringPlatformTransactionManager(org.springframework.transaction.support.CallbackPreferringPlatformTransactionManager) Publisher(org.reactivestreams.Publisher) Mono(reactor.core.publisher.Mono) ReactiveTransactionManager(org.springframework.transaction.ReactiveTransactionManager) NamedThreadLocal(org.springframework.core.NamedThreadLocal) Flux(reactor.core.publisher.Flux) Try(io.vavr.control.Try) NoTransactionException(org.springframework.transaction.NoTransactionException) PlatformTransactionManager(org.springframework.transaction.PlatformTransactionManager) BeanFactory(org.springframework.beans.factory.BeanFactory) TransactionStatus(org.springframework.transaction.TransactionStatus) Log(org.apache.commons.logging.Log) LogFactory(org.apache.commons.logging.LogFactory) Assert(org.springframework.util.Assert) StringUtils(org.springframework.util.StringUtils) TransactionStatus(org.springframework.transaction.TransactionStatus) TransactionSystemException(org.springframework.transaction.TransactionSystemException) CallbackPreferringPlatformTransactionManager(org.springframework.transaction.support.CallbackPreferringPlatformTransactionManager) ReactiveAdapter(org.springframework.core.ReactiveAdapter) Mono(reactor.core.publisher.Mono) Flux(reactor.core.publisher.Flux) Publisher(org.reactivestreams.Publisher) CallbackPreferringPlatformTransactionManager(org.springframework.transaction.support.CallbackPreferringPlatformTransactionManager) PlatformTransactionManager(org.springframework.transaction.PlatformTransactionManager) TransactionManager(org.springframework.transaction.TransactionManager) CallbackPreferringPlatformTransactionManager(org.springframework.transaction.support.CallbackPreferringPlatformTransactionManager) ReactiveTransactionManager(org.springframework.transaction.ReactiveTransactionManager) PlatformTransactionManager(org.springframework.transaction.PlatformTransactionManager) ReactiveTransactionManager(org.springframework.transaction.ReactiveTransactionManager) MethodParameter(org.springframework.core.MethodParameter) Nullable(org.springframework.lang.Nullable)

Aggregations

Try (io.vavr.control.Try)1 Method (java.lang.reflect.Method)1 Properties (java.util.Properties)1 ConcurrentMap (java.util.concurrent.ConcurrentMap)1 Continuation (kotlin.coroutines.Continuation)1 AwaitKt (kotlinx.coroutines.reactive.AwaitKt)1 ReactiveFlowKt (kotlinx.coroutines.reactive.ReactiveFlowKt)1 Log (org.apache.commons.logging.Log)1 LogFactory (org.apache.commons.logging.LogFactory)1 Publisher (org.reactivestreams.Publisher)1 BeanFactory (org.springframework.beans.factory.BeanFactory)1 BeanFactoryAware (org.springframework.beans.factory.BeanFactoryAware)1 InitializingBean (org.springframework.beans.factory.InitializingBean)1 BeanFactoryAnnotationUtils (org.springframework.beans.factory.annotation.BeanFactoryAnnotationUtils)1 CoroutinesUtils (org.springframework.core.CoroutinesUtils)1 KotlinDetector (org.springframework.core.KotlinDetector)1 MethodParameter (org.springframework.core.MethodParameter)1 NamedThreadLocal (org.springframework.core.NamedThreadLocal)1 ReactiveAdapter (org.springframework.core.ReactiveAdapter)1 ReactiveAdapterRegistry (org.springframework.core.ReactiveAdapterRegistry)1