/*
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.transaction.reactive;
import java.util.ArrayDeque;
import java.util.function.Function;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.util.context.Context;
import org.springframework.transaction.NoTransactionException;
Delegate to register and obtain transactional contexts.
Typically used by components that intercept or orchestrate transactional flows
such as AOP interceptors or transactional operators.
Author: Mark Paluch See Also: Since: 5.2
/**
* Delegate to register and obtain transactional contexts.
*
* <p>Typically used by components that intercept or orchestrate transactional flows
* such as AOP interceptors or transactional operators.
*
* @author Mark Paluch
* @since 5.2
* @see TransactionSynchronization
*/
public abstract class TransactionContextManager {
private TransactionContextManager() {
}
Obtain the current TransactionContext
from the subscriber context or the transactional context holder. Context retrieval fails with NoTransactionException if no context or context holder is registered. Throws: - NoTransactionException – if no TransactionContext was found in the subscriber context
or no context found in a holder
Returns: the current TransactionContext
/**
* Obtain the current {@link TransactionContext} from the subscriber context or the
* transactional context holder. Context retrieval fails with NoTransactionException
* if no context or context holder is registered.
* @return the current {@link TransactionContext}
* @throws NoTransactionException if no TransactionContext was found in the subscriber context
* or no context found in a holder
*/
public static Mono<TransactionContext> currentContext() throws NoTransactionException {
return Mono.deferContextual(ctx -> {
if (ctx.hasKey(TransactionContext.class)) {
return Mono.just(ctx.get(TransactionContext.class));
}
if (ctx.hasKey(TransactionContextHolder.class)) {
TransactionContextHolder holder = ctx.get(TransactionContextHolder.class);
if (holder.hasContext()) {
return Mono.just(holder.currentContext());
}
}
return Mono.error(new NoTransactionInContextException());
});
}
Create a TransactionContext
and register it in the subscriber Context
. Throws: - IllegalStateException – if a transaction context is already associated.
See Also: - subscriberContext.subscriberContext(Function)
- subscriberContext.subscriberContext(Function)
Returns: functional context registration.
/**
* Create a {@link TransactionContext} and register it in the subscriber {@link Context}.
* @return functional context registration.
* @throws IllegalStateException if a transaction context is already associated.
* @see Mono#subscriberContext(Function)
* @see Flux#subscriberContext(Function)
*/
public static Function<Context, Context> createTransactionContext() {
return context -> context.put(TransactionContext.class, new TransactionContext());
}
Return a Function
to create or associate a new TransactionContext
. Interaction with transactional resources through TransactionSynchronizationManager
requires a TransactionContext to be registered in the subscriber context. Returns: functional context registration.
/**
* Return a {@link Function} to create or associate a new {@link TransactionContext}.
* Interaction with transactional resources through
* {@link TransactionSynchronizationManager} requires a TransactionContext
* to be registered in the subscriber context.
* @return functional context registration.
*/
public static Function<Context, Context> getOrCreateContext() {
return context -> {
TransactionContextHolder holder = context.get(TransactionContextHolder.class);
if (holder.hasContext()) {
return context.put(TransactionContext.class, holder.currentContext());
}
return context.put(TransactionContext.class, holder.createContext());
};
}
Return a Function
to create or associate a new TransactionContextHolder
. Creation and release of transactions within a reactive flow requires a mutable holder that follows a top to down execution scheme. Reactor's subscriber context follows a down to top approach regarding mutation visibility. Returns: functional context registration.
/**
* Return a {@link Function} to create or associate a new
* {@link TransactionContextHolder}. Creation and release of transactions
* within a reactive flow requires a mutable holder that follows a top to
* down execution scheme. Reactor's subscriber context follows a down to top
* approach regarding mutation visibility.
* @return functional context registration.
*/
public static Function<Context, Context> getOrCreateContextHolder() {
return context -> {
if (!context.hasKey(TransactionContextHolder.class)) {
return context.put(TransactionContextHolder.class, new TransactionContextHolder(new ArrayDeque<>()));
}
return context;
};
}
Stackless variant of NoTransactionException
for reactive flows. /**
* Stackless variant of {@link NoTransactionException} for reactive flows.
*/
@SuppressWarnings("serial")
private static class NoTransactionInContextException extends NoTransactionException {
public NoTransactionInContextException() {
super("No transaction in context");
}
@Override
public synchronized Throwable fillInStackTrace() {
// stackless exception
return this;
}
}
}