|
|
|
@ -158,13 +158,73 @@ public abstract class TransactionSynchronizationManager { |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Bind the given resource for the given key to the current thread. |
|
|
|
* Bind the given resource for the given key to the current thread. |
|
|
|
|
|
|
|
* <p><b>Note: Any bound resource needs to get explicitly unbound through |
|
|
|
|
|
|
|
* {@link #unbindResource}. For automatic unbinding after transaction |
|
|
|
|
|
|
|
* completion, use {@link #bindSynchronizedResource} instead.</b> |
|
|
|
* @param key the key to bind the value to (usually the resource factory) |
|
|
|
* @param key the key to bind the value to (usually the resource factory) |
|
|
|
* @param value the value to bind (usually the active resource object) |
|
|
|
* @param value the value to bind (usually the active resource object) |
|
|
|
* @throws IllegalStateException if there is already a value bound to the thread |
|
|
|
* @throws IllegalStateException if there is already a value bound to the thread |
|
|
|
* @see ResourceTransactionManager#getResourceFactory() |
|
|
|
* @see ResourceTransactionManager#getResourceFactory() |
|
|
|
|
|
|
|
* @see #bindSynchronizedResource |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public static void bindResource(Object key, Object value) throws IllegalStateException { |
|
|
|
public static void bindResource(Object key, Object value) throws IllegalStateException { |
|
|
|
Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key); |
|
|
|
Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key); |
|
|
|
|
|
|
|
Object oldValue = doBindResource(actualKey, value); |
|
|
|
|
|
|
|
if (oldValue != null) { |
|
|
|
|
|
|
|
throw new IllegalStateException( |
|
|
|
|
|
|
|
"Already value [" + oldValue + "] for key [" + actualKey + "] bound to thread"); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Bind the given resource for the given key to the current thread, |
|
|
|
|
|
|
|
* synchronizing it with the current transaction for automatic unbinding |
|
|
|
|
|
|
|
* after transaction completion. |
|
|
|
|
|
|
|
* <p>This is effectively a programmatic way to register a transaction-scoped |
|
|
|
|
|
|
|
* resource, similar to the BeanFactory-driven {@link SimpleTransactionScope}. |
|
|
|
|
|
|
|
* <p>An existing value bound for the given key will be preserved and re-bound |
|
|
|
|
|
|
|
* after transaction completion, restoring the state before this bind call. |
|
|
|
|
|
|
|
* @param key the key to bind the value to (usually the resource factory) |
|
|
|
|
|
|
|
* @param value the value to bind (usually the active resource object) |
|
|
|
|
|
|
|
* @throws IllegalStateException if transaction synchronization is not active |
|
|
|
|
|
|
|
* @since 7.0 |
|
|
|
|
|
|
|
* @see #bindResource |
|
|
|
|
|
|
|
* @see #registerSynchronization |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
public static void bindSynchronizedResource(Object key, Object value) throws IllegalStateException { |
|
|
|
|
|
|
|
Set<TransactionSynchronization> synchs = synchronizations.get(); |
|
|
|
|
|
|
|
if (synchs == null) { |
|
|
|
|
|
|
|
throw new IllegalStateException("Transaction synchronization is not active"); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key); |
|
|
|
|
|
|
|
Object oldValue = doBindResource(actualKey, value); |
|
|
|
|
|
|
|
synchs.add(new TransactionSynchronization() { |
|
|
|
|
|
|
|
@Override |
|
|
|
|
|
|
|
public void suspend() { |
|
|
|
|
|
|
|
doUnbindResource(actualKey); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
|
|
|
|
public void resume() { |
|
|
|
|
|
|
|
Object existingValue = doBindResource(actualKey, value); |
|
|
|
|
|
|
|
if (existingValue != null) { |
|
|
|
|
|
|
|
throw new IllegalStateException( |
|
|
|
|
|
|
|
"Unexpected value [" + existingValue + "] for key [" + actualKey + "] bound on resume"); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
|
|
|
|
public void afterCompletion(int status) { |
|
|
|
|
|
|
|
doUnbindResource(actualKey); |
|
|
|
|
|
|
|
if (oldValue != null) { |
|
|
|
|
|
|
|
doBindResource(actualKey, oldValue); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Actually bind the given resource for the given key to the current thread. |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
private static @Nullable Object doBindResource(Object actualKey, Object value) { |
|
|
|
Assert.notNull(value, "Value must not be null"); |
|
|
|
Assert.notNull(value, "Value must not be null"); |
|
|
|
Map<Object, Object> map = resources.get(); |
|
|
|
Map<Object, Object> map = resources.get(); |
|
|
|
// set ThreadLocal Map if none found
|
|
|
|
// set ThreadLocal Map if none found
|
|
|
|
@ -177,18 +237,19 @@ public abstract class TransactionSynchronizationManager { |
|
|
|
if (oldValue instanceof ResourceHolder resourceHolder && resourceHolder.isVoid()) { |
|
|
|
if (oldValue instanceof ResourceHolder resourceHolder && resourceHolder.isVoid()) { |
|
|
|
oldValue = null; |
|
|
|
oldValue = null; |
|
|
|
} |
|
|
|
} |
|
|
|
if (oldValue != null) { |
|
|
|
return oldValue; |
|
|
|
throw new IllegalStateException( |
|
|
|
|
|
|
|
"Already value [" + oldValue + "] for key [" + actualKey + "] bound to thread"); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Unbind a resource for the given key from the current thread. |
|
|
|
* Unbind a resource for the given key from the current thread. |
|
|
|
|
|
|
|
* <p>This explicit step is only necessary with {@link #bindResource}. |
|
|
|
|
|
|
|
* For automatic unbinding, consider {@link #bindSynchronizedResource}. |
|
|
|
* @param key the key to unbind (usually the resource factory) |
|
|
|
* @param key the key to unbind (usually the resource factory) |
|
|
|
* @return the previously bound value (usually the active resource object) |
|
|
|
* @return the previously bound value (usually the active resource object) |
|
|
|
* @throws IllegalStateException if there is no value bound to the thread |
|
|
|
* @throws IllegalStateException if there is no value bound to the thread |
|
|
|
* @see ResourceTransactionManager#getResourceFactory() |
|
|
|
* @see ResourceTransactionManager#getResourceFactory() |
|
|
|
|
|
|
|
* @see #bindResource |
|
|
|
|
|
|
|
* @see #unbindResourceIfPossible |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public static Object unbindResource(Object key) throws IllegalStateException { |
|
|
|
public static Object unbindResource(Object key) throws IllegalStateException { |
|
|
|
Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key); |
|
|
|
Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key); |
|
|
|
@ -201,8 +262,12 @@ public abstract class TransactionSynchronizationManager { |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Unbind a resource for the given key from the current thread. |
|
|
|
* Unbind a resource for the given key from the current thread. |
|
|
|
|
|
|
|
* <p>This explicit step is only necessary with {@link #bindResource}. |
|
|
|
|
|
|
|
* For automatic unbinding, consider {@link #bindSynchronizedResource}. |
|
|
|
* @param key the key to unbind (usually the resource factory) |
|
|
|
* @param key the key to unbind (usually the resource factory) |
|
|
|
* @return the previously bound value, or {@code null} if none bound |
|
|
|
* @return the previously bound value, or {@code null} if none bound |
|
|
|
|
|
|
|
* @see #bindResource |
|
|
|
|
|
|
|
* @see #unbindResource |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public static @Nullable Object unbindResourceIfPossible(Object key) { |
|
|
|
public static @Nullable Object unbindResourceIfPossible(Object key) { |
|
|
|
Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key); |
|
|
|
Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key); |
|
|
|
|