Browse Source

Polishing

(cherry picked from commit 6e5af9dccb)
pull/31598/head
Juergen Hoeller 1 year ago
parent
commit
9d7154901f
  1. 12
      integration-tests/src/test/java/org/springframework/scheduling/annotation/ScheduledAndTransactionalAnnotationIntegrationTests.java
  2. 14
      spring-aop/src/test/java/org/springframework/aop/framework/adapter/ThrowsAdviceInterceptorTests.java
  3. 26
      spring-context-support/src/test/java/org/springframework/scheduling/quartz/QuartzSupportTests.java
  4. 18
      spring-context/src/main/java/org/springframework/context/annotation/ClassPathBeanDefinitionScanner.java
  5. 2
      spring-context/src/test/java/org/springframework/context/annotation/ClassPathBeanDefinitionScannerTests.java
  6. 23
      spring-core/src/main/java/org/springframework/core/ReactiveAdapterRegistry.java
  7. 14
      spring-core/src/main/java/org/springframework/core/ReactiveTypeDescriptor.java
  8. 26
      spring-core/src/test/java/org/springframework/core/ReactiveAdapterRegistryTests.java
  9. 100
      spring-r2dbc/src/test/java/org/springframework/r2dbc/connection/R2dbcTransactionManagerUnitTests.java
  10. 3
      spring-tx/src/main/java/org/springframework/transaction/PlatformTransactionManager.java
  11. 5
      spring-tx/src/main/java/org/springframework/transaction/ReactiveTransaction.java
  12. 5
      spring-tx/src/main/java/org/springframework/transaction/TransactionStatus.java
  13. 8
      spring-tx/src/main/java/org/springframework/transaction/support/SmartTransactionObject.java

12
integration-tests/src/test/java/org/springframework/scheduling/annotation/ScheduledAndTransactionalAnnotationIntegrationTests.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 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.
@ -60,8 +60,8 @@ class ScheduledAndTransactionalAnnotationIntegrationTests { @@ -60,8 +60,8 @@ class ScheduledAndTransactionalAnnotationIntegrationTests {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(Config.class, JdkProxyTxConfig.class, RepoConfigA.class);
assertThatExceptionOfType(BeanCreationException.class)
.isThrownBy(ctx::refresh)
.withCauseInstanceOf(IllegalStateException.class);
.isThrownBy(ctx::refresh)
.withCauseInstanceOf(IllegalStateException.class);
}
@Test
@ -70,7 +70,7 @@ class ScheduledAndTransactionalAnnotationIntegrationTests { @@ -70,7 +70,7 @@ class ScheduledAndTransactionalAnnotationIntegrationTests {
ctx.register(Config.class, SubclassProxyTxConfig.class, RepoConfigA.class);
ctx.refresh();
Thread.sleep(100); // allow @Scheduled method to be called several times
Thread.sleep(200); // allow @Scheduled method to be called several times
MyRepository repository = ctx.getBean(MyRepository.class);
CallCountingTransactionManager txManager = ctx.getBean(CallCountingTransactionManager.class);
@ -85,7 +85,7 @@ class ScheduledAndTransactionalAnnotationIntegrationTests { @@ -85,7 +85,7 @@ class ScheduledAndTransactionalAnnotationIntegrationTests {
ctx.register(Config.class, JdkProxyTxConfig.class, RepoConfigB.class);
ctx.refresh();
Thread.sleep(100); // allow @Scheduled method to be called several times
Thread.sleep(200); // allow @Scheduled method to be called several times
MyRepositoryWithScheduledMethod repository = ctx.getBean(MyRepositoryWithScheduledMethod.class);
CallCountingTransactionManager txManager = ctx.getBean(CallCountingTransactionManager.class);
@ -100,7 +100,7 @@ class ScheduledAndTransactionalAnnotationIntegrationTests { @@ -100,7 +100,7 @@ class ScheduledAndTransactionalAnnotationIntegrationTests {
ctx.register(AspectConfig.class, MyRepositoryWithScheduledMethodImpl.class);
ctx.refresh();
Thread.sleep(100); // allow @Scheduled method to be called several times
Thread.sleep(200); // allow @Scheduled method to be called several times
MyRepositoryWithScheduledMethod repository = ctx.getBean(MyRepositoryWithScheduledMethod.class);
assertThat(AopUtils.isCglibProxy(repository)).isTrue();

14
spring-aop/src/test/java/org/springframework/aop/framework/adapter/ThrowsAdviceInterceptorTests.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 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.
@ -77,9 +77,7 @@ public class ThrowsAdviceInterceptorTests { @@ -77,9 +77,7 @@ public class ThrowsAdviceInterceptorTests {
given(mi.getMethod()).willReturn(Object.class.getMethod("hashCode"));
given(mi.getThis()).willReturn(new Object());
given(mi.proceed()).willThrow(ex);
assertThatExceptionOfType(FileNotFoundException.class).isThrownBy(() ->
ti.invoke(mi))
.isSameAs(ex);
assertThatExceptionOfType(FileNotFoundException.class).isThrownBy(() -> ti.invoke(mi)).isSameAs(ex);
assertThat(th.getCalls()).isEqualTo(1);
assertThat(th.getCalls("ioException")).isEqualTo(1);
}
@ -92,9 +90,7 @@ public class ThrowsAdviceInterceptorTests { @@ -92,9 +90,7 @@ public class ThrowsAdviceInterceptorTests {
ConnectException ex = new ConnectException("");
MethodInvocation mi = mock(MethodInvocation.class);
given(mi.proceed()).willThrow(ex);
assertThatExceptionOfType(ConnectException.class).isThrownBy(() ->
ti.invoke(mi))
.isSameAs(ex);
assertThatExceptionOfType(ConnectException.class).isThrownBy(() -> ti.invoke(mi)).isSameAs(ex);
assertThat(th.getCalls()).isEqualTo(1);
assertThat(th.getCalls("remoteException")).isEqualTo(1);
}
@ -117,9 +113,7 @@ public class ThrowsAdviceInterceptorTests { @@ -117,9 +113,7 @@ public class ThrowsAdviceInterceptorTests {
ConnectException ex = new ConnectException("");
MethodInvocation mi = mock(MethodInvocation.class);
given(mi.proceed()).willThrow(ex);
assertThatExceptionOfType(Throwable.class).isThrownBy(() ->
ti.invoke(mi))
.isSameAs(t);
assertThatExceptionOfType(Throwable.class).isThrownBy(() -> ti.invoke(mi)).isSameAs(t);
assertThat(th.getCalls()).isEqualTo(1);
assertThat(th.getCalls("remoteException")).isEqualTo(1);
}

26
spring-context-support/src/test/java/org/springframework/scheduling/quartz/QuartzSupportTests.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2023 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.
@ -114,7 +114,7 @@ class QuartzSupportTests { @@ -114,7 +114,7 @@ class QuartzSupportTests {
trigger.setName("myTrigger");
trigger.setJobDetail(jobDetail);
trigger.setStartDelay(1);
trigger.setRepeatInterval(500);
trigger.setRepeatInterval(100);
trigger.setRepeatCount(1);
trigger.afterPropertiesSet();
@ -126,14 +126,14 @@ class QuartzSupportTests { @@ -126,14 +126,14 @@ class QuartzSupportTests {
bean.start();
Thread.sleep(500);
assertThat(DummyJob.count > 0).as("DummyJob should have been executed at least once.").isTrue();
assertThat(DummyJob.count).as("DummyJob should have been executed at least once.").isGreaterThan(0);
assertThat(taskExecutor.count).isEqualTo(DummyJob.count);
bean.destroy();
}
@Test
@SuppressWarnings({ "unchecked", "rawtypes" })
@SuppressWarnings({"unchecked", "rawtypes"})
void jobDetailWithRunnableInsteadOfJob() {
JobDetailImpl jobDetail = new JobDetailImpl();
assertThatIllegalArgumentException().isThrownBy(() ->
@ -156,7 +156,7 @@ class QuartzSupportTests { @@ -156,7 +156,7 @@ class QuartzSupportTests {
trigger.setName("myTrigger");
trigger.setJobDetail(jobDetail);
trigger.setStartDelay(1);
trigger.setRepeatInterval(500);
trigger.setRepeatInterval(100);
trigger.setRepeatCount(1);
trigger.afterPropertiesSet();
@ -168,7 +168,7 @@ class QuartzSupportTests { @@ -168,7 +168,7 @@ class QuartzSupportTests {
Thread.sleep(500);
assertThat(DummyJobBean.param).isEqualTo(10);
assertThat(DummyJobBean.count > 0).isTrue();
assertThat(DummyJobBean.count).isGreaterThan(0);
bean.destroy();
}
@ -190,7 +190,7 @@ class QuartzSupportTests { @@ -190,7 +190,7 @@ class QuartzSupportTests {
trigger.setName("myTrigger");
trigger.setJobDetail(jobDetail);
trigger.setStartDelay(1);
trigger.setRepeatInterval(500);
trigger.setRepeatInterval(100);
trigger.setRepeatCount(1);
trigger.afterPropertiesSet();
@ -203,7 +203,7 @@ class QuartzSupportTests { @@ -203,7 +203,7 @@ class QuartzSupportTests {
Thread.sleep(500);
assertThat(DummyJob.param).isEqualTo(10);
assertThat(DummyJob.count > 0).as("DummyJob should have been executed at least once.").isTrue();
assertThat(DummyJob.count).as("DummyJob should have been executed at least once.").isGreaterThan(0);
bean.destroy();
}
@ -225,7 +225,7 @@ class QuartzSupportTests { @@ -225,7 +225,7 @@ class QuartzSupportTests {
trigger.setName("myTrigger");
trigger.setJobDetail(jobDetail);
trigger.setStartDelay(1);
trigger.setRepeatInterval(500);
trigger.setRepeatInterval(100);
trigger.setRepeatCount(1);
trigger.afterPropertiesSet();
@ -239,7 +239,7 @@ class QuartzSupportTests { @@ -239,7 +239,7 @@ class QuartzSupportTests {
Thread.sleep(500);
assertThat(DummyJob.param).isEqualTo(0);
assertThat(DummyJob.count == 0).isTrue();
assertThat(DummyJob.count).isEqualTo(0);
bean.destroy();
}
@ -260,7 +260,7 @@ class QuartzSupportTests { @@ -260,7 +260,7 @@ class QuartzSupportTests {
trigger.setName("myTrigger");
trigger.setJobDetail(jobDetail);
trigger.setStartDelay(1);
trigger.setRepeatInterval(500);
trigger.setRepeatInterval(100);
trigger.setRepeatCount(1);
trigger.afterPropertiesSet();
@ -273,7 +273,7 @@ class QuartzSupportTests { @@ -273,7 +273,7 @@ class QuartzSupportTests {
Thread.sleep(500);
assertThat(DummyJobBean.param).isEqualTo(10);
assertThat(DummyJobBean.count > 0).isTrue();
assertThat(DummyJobBean.count).isGreaterThan(0);
bean.destroy();
}
@ -292,7 +292,7 @@ class QuartzSupportTests { @@ -292,7 +292,7 @@ class QuartzSupportTests {
Thread.sleep(500);
assertThat(DummyJob.param).isEqualTo(10);
assertThat(DummyJob.count > 0).as("DummyJob should have been executed at least once.").isTrue();
assertThat(DummyJob.count).as("DummyJob should have been executed at least once.").isGreaterThan(0);
bean.destroy();
}

18
spring-context/src/main/java/org/springframework/context/annotation/ClassPathBeanDefinitionScanner.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2023 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.
@ -329,8 +329,8 @@ public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateCo @@ -329,8 +329,8 @@ public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateCo
* @return {@code true} if the bean can be registered as-is;
* {@code false} if it should be skipped because there is an
* existing, compatible bean definition for the specified name
* @throws ConflictingBeanDefinitionException if an existing, incompatible
* bean definition has been found for the specified name
* @throws IllegalStateException if an existing, incompatible bean definition
* has been found for the specified name
*/
protected boolean checkCandidate(String beanName, BeanDefinition beanDefinition) throws IllegalStateException {
if (!this.registry.containsBeanDefinition(beanName)) {
@ -354,16 +354,16 @@ public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateCo @@ -354,16 +354,16 @@ public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateCo
* the given existing bean definition.
* <p>The default implementation considers them as compatible when the existing
* bean definition comes from the same source or from a non-scanning source.
* @param newDefinition the new bean definition, originated from scanning
* @param existingDefinition the existing bean definition, potentially an
* @param newDef the new bean definition, originated from scanning
* @param existingDef the existing bean definition, potentially an
* explicitly defined one or a previously generated one from scanning
* @return whether the definitions are considered as compatible, with the
* new definition to be skipped in favor of the existing definition
*/
protected boolean isCompatible(BeanDefinition newDefinition, BeanDefinition existingDefinition) {
return (!(existingDefinition instanceof ScannedGenericBeanDefinition) || // explicitly registered overriding bean
(newDefinition.getSource() != null && newDefinition.getSource().equals(existingDefinition.getSource())) || // scanned same file twice
newDefinition.equals(existingDefinition)); // scanned equivalent class twice
protected boolean isCompatible(BeanDefinition newDef, BeanDefinition existingDef) {
return (!(existingDef instanceof ScannedGenericBeanDefinition) || // explicitly registered overriding bean
(newDef.getSource() != null && newDef.getSource().equals(existingDef.getSource())) || // scanned same file twice
newDef.equals(existingDef)); // scanned equivalent class twice
}

2
spring-context/src/test/java/org/springframework/context/annotation/ClassPathBeanDefinitionScannerTests.java

@ -197,6 +197,7 @@ public class ClassPathBeanDefinitionScannerTests { @@ -197,6 +197,7 @@ public class ClassPathBeanDefinitionScannerTests {
context.registerBeanDefinition("stubFooDao", new RootBeanDefinition(TestBean.class));
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context);
scanner.setIncludeAnnotationConfig(false);
// should not fail!
scanner.scan(BASE_PACKAGE);
}
@ -207,6 +208,7 @@ public class ClassPathBeanDefinitionScannerTests { @@ -207,6 +208,7 @@ public class ClassPathBeanDefinitionScannerTests {
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context);
scanner.setIncludeAnnotationConfig(false);
scanner.scan("org.springframework.context.annotation3");
assertThatIllegalStateException().isThrownBy(() -> scanner.scan(BASE_PACKAGE))
.withMessageContaining("stubFooDao")
.withMessageContaining(StubFooDao.class.getName());

23
spring-core/src/main/java/org/springframework/core/ReactiveAdapterRegistry.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2023 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.
@ -24,8 +24,6 @@ import java.util.concurrent.CompletableFuture; @@ -24,8 +24,6 @@ import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.function.Function;
import kotlinx.coroutines.CompletableDeferredKt;
import kotlinx.coroutines.Deferred;
import org.reactivestreams.Publisher;
import reactor.blockhound.BlockHound;
import reactor.blockhound.integration.BlockHoundIntegration;
@ -39,13 +37,14 @@ import org.springframework.util.ConcurrentReferenceHashMap; @@ -39,13 +37,14 @@ import org.springframework.util.ConcurrentReferenceHashMap;
import org.springframework.util.ReflectionUtils;
/**
* A registry of adapters to adapt Reactive Streams {@link Publisher} to/from
* various async/reactive types such as {@code CompletableFuture}, RxJava
* {@code Flowable}, and others.
* A registry of adapters to adapt Reactive Streams {@link Publisher} to/from various
* async/reactive types such as {@code CompletableFuture}, RxJava {@code Flowable}, etc.
* This is designed to complement Spring's Reactor {@code Mono}/{@code Flux} support while
* also being usable without Reactor, e.g. just for {@code org.reactivestreams} bridging.
*
* <p>By default, depending on classpath availability, adapters are registered
* for Reactor, RxJava 3, {@link CompletableFuture}, {@code Flow.Publisher},
* and Kotlin Coroutines' {@code Deferred} and {@code Flow}.
* <p>By default, depending on classpath availability, adapters are registered for Reactor
* (including {@code CompletableFuture} and {@code Flow.Publisher} adapters), RxJava 3,
* Kotlin Coroutines' {@code Deferred} (bridged via Reactor) and SmallRye Mutiny 1.x.
*
* <p><strong>Note:</strong> As of Spring Framework 5.3.11, support for
* RxJava 1.x and 2.x is deprecated in favor of RxJava 3.
@ -401,9 +400,9 @@ public class ReactiveAdapterRegistry { @@ -401,9 +400,9 @@ public class ReactiveAdapterRegistry {
@SuppressWarnings("KotlinInternalInJava")
void registerAdapters(ReactiveAdapterRegistry registry) {
registry.registerReactiveType(
ReactiveTypeDescriptor.singleOptionalValue(Deferred.class,
() -> CompletableDeferredKt.CompletableDeferred(null)),
source -> CoroutinesUtils.deferredToMono((Deferred<?>) source),
ReactiveTypeDescriptor.singleOptionalValue(kotlinx.coroutines.Deferred.class,
() -> kotlinx.coroutines.CompletableDeferredKt.CompletableDeferred(null)),
source -> CoroutinesUtils.deferredToMono((kotlinx.coroutines.Deferred<?>) source),
source -> CoroutinesUtils.monoToDeferred(Mono.from(source)));
registry.registerReactiveType(

14
spring-core/src/main/java/org/springframework/core/ReactiveTypeDescriptor.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2023 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.
@ -37,7 +37,7 @@ public final class ReactiveTypeDescriptor { @@ -37,7 +37,7 @@ public final class ReactiveTypeDescriptor {
private final boolean noValue;
@Nullable
private final Supplier<?> emptyValueSupplier;
private final Supplier<?> emptySupplier;
private final boolean deferred;
@ -55,7 +55,7 @@ public final class ReactiveTypeDescriptor { @@ -55,7 +55,7 @@ public final class ReactiveTypeDescriptor {
this.reactiveType = reactiveType;
this.multiValue = multiValue;
this.noValue = noValue;
this.emptyValueSupplier = emptySupplier;
this.emptySupplier = emptySupplier;
this.deferred = deferred;
}
@ -89,16 +89,16 @@ public final class ReactiveTypeDescriptor { @@ -89,16 +89,16 @@ public final class ReactiveTypeDescriptor {
* Return {@code true} if the reactive type can complete with no values.
*/
public boolean supportsEmpty() {
return (this.emptyValueSupplier != null);
return (this.emptySupplier != null);
}
/**
* Return an empty-value instance for the underlying reactive or async type.
* Use of this type implies {@link #supportsEmpty()} is {@code true}.
* <p>Use of this type implies {@link #supportsEmpty()} is {@code true}.
*/
public Object getEmptyValue() {
Assert.state(this.emptyValueSupplier != null, "Empty values not supported");
return this.emptyValueSupplier.get();
Assert.state(this.emptySupplier != null, "Empty values not supported");
return this.emptySupplier.get();
}
/**

26
spring-core/src/test/java/org/springframework/core/ReactiveAdapterRegistryTests.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 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.
@ -100,7 +100,7 @@ class ReactiveAdapterRegistryTests { @@ -100,7 +100,7 @@ class ReactiveAdapterRegistryTests {
List<Integer> sequence = Arrays.asList(1, 2, 3);
Publisher<Integer> source = io.reactivex.rxjava3.core.Flowable.fromIterable(sequence);
Object target = getAdapter(Flux.class).fromPublisher(source);
assertThat(target instanceof Flux).isTrue();
assertThat(target).isInstanceOf(Flux.class);
assertThat(((Flux<Integer>) target).collectList().block(ONE_SECOND)).isEqualTo(sequence);
}
@ -108,7 +108,7 @@ class ReactiveAdapterRegistryTests { @@ -108,7 +108,7 @@ class ReactiveAdapterRegistryTests {
void toMono() {
Publisher<Integer> source = io.reactivex.rxjava3.core.Flowable.fromArray(1, 2, 3);
Object target = getAdapter(Mono.class).fromPublisher(source);
assertThat(target instanceof Mono).isTrue();
assertThat(target).isInstanceOf(Mono.class);
assertThat(((Mono<Integer>) target).block(ONE_SECOND)).isEqualTo(Integer.valueOf(1));
}
@ -116,7 +116,7 @@ class ReactiveAdapterRegistryTests { @@ -116,7 +116,7 @@ class ReactiveAdapterRegistryTests {
void toCompletableFuture() throws Exception {
Publisher<Integer> source = Flux.fromArray(new Integer[] {1, 2, 3});
Object target = getAdapter(CompletableFuture.class).fromPublisher(source);
assertThat(target instanceof CompletableFuture).isTrue();
assertThat(target).isInstanceOf(CompletableFuture.class);
assertThat(((CompletableFuture<Integer>) target).get()).isEqualTo(Integer.valueOf(1));
}
@ -125,7 +125,7 @@ class ReactiveAdapterRegistryTests { @@ -125,7 +125,7 @@ class ReactiveAdapterRegistryTests {
CompletableFuture<Integer> future = new CompletableFuture<>();
future.complete(1);
Object target = getAdapter(CompletableFuture.class).toPublisher(future);
assertThat(target instanceof Mono).as("Expected Mono Publisher: " + target.getClass().getName()).isTrue();
assertThat(target).as("Expected Mono Publisher: " + target.getClass().getName()).isInstanceOf(Mono.class);
assertThat(((Mono<Integer>) target).block(ONE_SECOND)).isEqualTo(Integer.valueOf(1));
}
}
@ -294,7 +294,7 @@ class ReactiveAdapterRegistryTests { @@ -294,7 +294,7 @@ class ReactiveAdapterRegistryTests {
List<Integer> sequence = Arrays.asList(1, 2, 3);
Publisher<Integer> source = Flux.fromIterable(sequence);
Object target = getAdapter(io.reactivex.rxjava3.core.Flowable.class).fromPublisher(source);
assertThat(target instanceof io.reactivex.rxjava3.core.Flowable).isTrue();
assertThat(target).isInstanceOf(io.reactivex.rxjava3.core.Flowable.class);
assertThat(((io.reactivex.rxjava3.core.Flowable<?>) target).toList().blockingGet()).isEqualTo(sequence);
}
@ -303,7 +303,7 @@ class ReactiveAdapterRegistryTests { @@ -303,7 +303,7 @@ class ReactiveAdapterRegistryTests {
List<Integer> sequence = Arrays.asList(1, 2, 3);
Publisher<Integer> source = Flux.fromIterable(sequence);
Object target = getAdapter(io.reactivex.rxjava3.core.Observable.class).fromPublisher(source);
assertThat(target instanceof io.reactivex.rxjava3.core.Observable).isTrue();
assertThat(target).isInstanceOf(io.reactivex.rxjava3.core.Observable.class);
assertThat(((io.reactivex.rxjava3.core.Observable<?>) target).toList().blockingGet()).isEqualTo(sequence);
}
@ -311,7 +311,7 @@ class ReactiveAdapterRegistryTests { @@ -311,7 +311,7 @@ class ReactiveAdapterRegistryTests {
void toSingle() {
Publisher<Integer> source = Flux.fromArray(new Integer[] {1});
Object target = getAdapter(io.reactivex.rxjava3.core.Single.class).fromPublisher(source);
assertThat(target instanceof io.reactivex.rxjava3.core.Single).isTrue();
assertThat(target).isInstanceOf(io.reactivex.rxjava3.core.Single.class);
assertThat(((io.reactivex.rxjava3.core.Single<Integer>) target).blockingGet()).isEqualTo(Integer.valueOf(1));
}
@ -319,7 +319,7 @@ class ReactiveAdapterRegistryTests { @@ -319,7 +319,7 @@ class ReactiveAdapterRegistryTests {
void toCompletable() {
Publisher<Integer> source = Flux.fromArray(new Integer[] {1, 2, 3});
Object target = getAdapter(io.reactivex.rxjava3.core.Completable.class).fromPublisher(source);
assertThat(target instanceof io.reactivex.rxjava3.core.Completable).isTrue();
assertThat(target).isInstanceOf(io.reactivex.rxjava3.core.Completable.class);
((io.reactivex.rxjava3.core.Completable) target).blockingAwait();
}
@ -328,7 +328,7 @@ class ReactiveAdapterRegistryTests { @@ -328,7 +328,7 @@ class ReactiveAdapterRegistryTests {
List<Integer> sequence = Arrays.asList(1, 2, 3);
Object source = io.reactivex.rxjava3.core.Flowable.fromIterable(sequence);
Object target = getAdapter(io.reactivex.rxjava3.core.Flowable.class).toPublisher(source);
assertThat(target instanceof Flux).as("Expected Flux Publisher: " + target.getClass().getName()).isTrue();
assertThat(target).as("Expected Flux Publisher: " + target.getClass().getName()).isInstanceOf(Flux.class);
assertThat(((Flux<Integer>) target).collectList().block(ONE_SECOND)).isEqualTo(sequence);
}
@ -337,7 +337,7 @@ class ReactiveAdapterRegistryTests { @@ -337,7 +337,7 @@ class ReactiveAdapterRegistryTests {
List<Integer> sequence = Arrays.asList(1, 2, 3);
Object source = io.reactivex.rxjava3.core.Observable.fromIterable(sequence);
Object target = getAdapter(io.reactivex.rxjava3.core.Observable.class).toPublisher(source);
assertThat(target instanceof Flux).as("Expected Flux Publisher: " + target.getClass().getName()).isTrue();
assertThat(target).as("Expected Flux Publisher: " + target.getClass().getName()).isInstanceOf(Flux.class);
assertThat(((Flux<Integer>) target).collectList().block(ONE_SECOND)).isEqualTo(sequence);
}
@ -345,7 +345,7 @@ class ReactiveAdapterRegistryTests { @@ -345,7 +345,7 @@ class ReactiveAdapterRegistryTests {
void fromSingle() {
Object source = io.reactivex.rxjava3.core.Single.just(1);
Object target = getAdapter(io.reactivex.rxjava3.core.Single.class).toPublisher(source);
assertThat(target instanceof Mono).as("Expected Mono Publisher: " + target.getClass().getName()).isTrue();
assertThat(target).as("Expected Mono Publisher: " + target.getClass().getName()).isInstanceOf(Mono.class);
assertThat(((Mono<Integer>) target).block(ONE_SECOND)).isEqualTo(Integer.valueOf(1));
}
@ -353,7 +353,7 @@ class ReactiveAdapterRegistryTests { @@ -353,7 +353,7 @@ class ReactiveAdapterRegistryTests {
void fromCompletable() {
Object source = io.reactivex.rxjava3.core.Completable.complete();
Object target = getAdapter(io.reactivex.rxjava3.core.Completable.class).toPublisher(source);
assertThat(target instanceof Mono).as("Expected Mono Publisher: " + target.getClass().getName()).isTrue();
assertThat(target).as("Expected Mono Publisher: " + target.getClass().getName()).isInstanceOf(Mono.class);
((Mono<Void>) target).block(ONE_SECOND);
}
}

100
spring-r2dbc/src/test/java/org/springframework/r2dbc/connection/R2dbcTransactionManagerUnitTests.java

@ -24,7 +24,6 @@ import io.r2dbc.spi.IsolationLevel; @@ -24,7 +24,6 @@ import io.r2dbc.spi.IsolationLevel;
import io.r2dbc.spi.R2dbcBadGrammarException;
import io.r2dbc.spi.R2dbcTimeoutException;
import io.r2dbc.spi.Statement;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import reactor.core.publisher.Mono;
@ -55,6 +54,7 @@ import static org.mockito.BDDMockito.when; @@ -55,6 +54,7 @@ import static org.mockito.BDDMockito.when;
* Unit tests for {@link R2dbcTransactionManager}.
*
* @author Mark Paluch
* @author Juergen Hoeller
*/
class R2dbcTransactionManagerUnitTests {
@ -85,8 +85,7 @@ class R2dbcTransactionManagerUnitTests { @@ -85,8 +85,7 @@ class R2dbcTransactionManagerUnitTests {
ConnectionFactoryUtils.getConnection(connectionFactoryMock)
.flatMap(connection -> TransactionSynchronizationManager.forCurrentTransaction()
.doOnNext(synchronizationManager -> synchronizationManager.registerSynchronization(
sync)))
.doOnNext(synchronizationManager -> synchronizationManager.registerSynchronization(sync)))
.as(operator::transactional)
.as(StepVerifier::create)
.expectNextCount(1)
@ -118,12 +117,11 @@ class R2dbcTransactionManagerUnitTests { @@ -118,12 +117,11 @@ class R2dbcTransactionManagerUnitTests {
TransactionalOperator operator = TransactionalOperator.create(tm, definition);
ConnectionFactoryUtils.getConnection(connectionFactoryMock).as(
operator::transactional)
ConnectionFactoryUtils.getConnection(connectionFactoryMock)
.as(operator::transactional)
.as(StepVerifier::create)
.expectErrorSatisfies(actual -> assertThat(actual).isInstanceOf(
CannotCreateTransactionException.class).hasCauseInstanceOf(
R2dbcBadGrammarException.class))
CannotCreateTransactionException.class).hasCauseInstanceOf(R2dbcBadGrammarException.class))
.verify();
}
@ -139,8 +137,8 @@ class R2dbcTransactionManagerUnitTests { @@ -139,8 +137,8 @@ class R2dbcTransactionManagerUnitTests {
TransactionalOperator operator = TransactionalOperator.create(tm, definition);
ConnectionFactoryUtils.getConnection(connectionFactoryMock).as(
operator::transactional)
ConnectionFactoryUtils.getConnection(connectionFactoryMock)
.as(operator::transactional)
.as(StepVerifier::create)
.expectNextCount(1)
.verifyComplete();
@ -164,8 +162,8 @@ class R2dbcTransactionManagerUnitTests { @@ -164,8 +162,8 @@ class R2dbcTransactionManagerUnitTests {
TransactionalOperator operator = TransactionalOperator.create(tm, definition);
ConnectionFactoryUtils.getConnection(connectionFactoryMock).as(
operator::transactional)
ConnectionFactoryUtils.getConnection(connectionFactoryMock)
.as(operator::transactional)
.as(StepVerifier::create)
.expectNextCount(1)
.verifyComplete();
@ -184,8 +182,8 @@ class R2dbcTransactionManagerUnitTests { @@ -184,8 +182,8 @@ class R2dbcTransactionManagerUnitTests {
TransactionalOperator operator = TransactionalOperator.create(tm, definition);
ConnectionFactoryUtils.getConnection(connectionFactoryMock).as(
operator::transactional)
ConnectionFactoryUtils.getConnection(connectionFactoryMock)
.as(operator::transactional)
.as(StepVerifier::create)
.expectNextCount(1)
.verifyComplete();
@ -232,8 +230,8 @@ class R2dbcTransactionManagerUnitTests { @@ -232,8 +230,8 @@ class R2dbcTransactionManagerUnitTests {
TransactionalOperator operator = TransactionalOperator.create(tm, definition);
ConnectionFactoryUtils.getConnection(connectionFactoryMock).as(
operator::transactional)
ConnectionFactoryUtils.getConnection(connectionFactoryMock)
.as(operator::transactional)
.as(StepVerifier::create)
.expectNextCount(1)
.verifyComplete();
@ -249,7 +247,6 @@ class R2dbcTransactionManagerUnitTests { @@ -249,7 +247,6 @@ class R2dbcTransactionManagerUnitTests {
@Test
void testCommitFails() {
when(connectionMock.commitTransaction()).thenReturn(Mono.defer(() -> Mono.error(new R2dbcBadGrammarException("Commit should fail"))));
when(connectionMock.rollbackTransaction()).thenReturn(Mono.empty());
TransactionalOperator operator = TransactionalOperator.create(tm);
@ -270,7 +267,6 @@ class R2dbcTransactionManagerUnitTests { @@ -270,7 +267,6 @@ class R2dbcTransactionManagerUnitTests {
@Test
void testRollback() {
AtomicInteger commits = new AtomicInteger();
when(connectionMock.commitTransaction()).thenReturn(
Mono.fromRunnable(commits::incrementAndGet));
@ -282,11 +278,9 @@ class R2dbcTransactionManagerUnitTests { @@ -282,11 +278,9 @@ class R2dbcTransactionManagerUnitTests {
TransactionalOperator operator = TransactionalOperator.create(tm);
ConnectionFactoryUtils.getConnection(connectionFactoryMock)
.doOnNext(connection -> {
throw new IllegalStateException();
}).as(operator::transactional)
.as(StepVerifier::create)
.verifyError(IllegalStateException.class);
.doOnNext(connection -> { throw new IllegalStateException(); })
.as(operator::transactional)
.as(StepVerifier::create).verifyError(IllegalStateException.class);
assertThat(commits).hasValue(0);
assertThat(rollbacks).hasValue(1);
@ -303,15 +297,11 @@ class R2dbcTransactionManagerUnitTests { @@ -303,15 +297,11 @@ class R2dbcTransactionManagerUnitTests {
when(connectionMock.rollbackTransaction()).thenReturn(Mono.defer(() -> Mono.error(new R2dbcBadGrammarException("Commit should fail"))), Mono.empty());
TransactionalOperator operator = TransactionalOperator.create(tm);
operator.execute(reactiveTransaction -> {
reactiveTransaction.setRollbackOnly();
return ConnectionFactoryUtils.getConnection(connectionFactoryMock)
.doOnNext(connection -> connection.createStatement("foo")).then();
}).as(StepVerifier::create)
.verifyError(IllegalTransactionStateException.class);
}).as(StepVerifier::create).verifyError(IllegalTransactionStateException.class);
verify(connectionMock).isAutoCommit();
verify(connectionMock).beginTransaction();
@ -338,7 +328,7 @@ class R2dbcTransactionManagerUnitTests { @@ -338,7 +328,7 @@ class R2dbcTransactionManagerUnitTests {
.doOnNext(connection -> {
throw new IllegalStateException("Intentional error to trigger rollback");
}).then()).as(StepVerifier::create)
.verifyErrorSatisfies(e -> Assertions.assertThat(e)
.verifyErrorSatisfies(ex -> assertThat(ex)
.isInstanceOf(BadSqlGrammarException.class)
.hasCause(new R2dbcBadGrammarException("Rollback should fail"))
);
@ -357,19 +347,15 @@ class R2dbcTransactionManagerUnitTests { @@ -357,19 +347,15 @@ class R2dbcTransactionManagerUnitTests {
TransactionSynchronization.STATUS_ROLLED_BACK);
TransactionalOperator operator = TransactionalOperator.create(tm);
operator.execute(tx -> {
tx.setRollbackOnly();
assertThat(tx.isNewTransaction()).isTrue();
return TransactionSynchronizationManager.forCurrentTransaction().doOnNext(
synchronizationManager -> {
assertThat(synchronizationManager.hasResource(connectionFactoryMock)).isTrue();
synchronizationManager.registerSynchronization(sync);
}).then();
}).as(StepVerifier::create)
.verifyComplete();
}).as(StepVerifier::create).verifyComplete();
verify(connectionMock).isAutoCommit();
verify(connectionMock).beginTransaction();
@ -389,20 +375,16 @@ class R2dbcTransactionManagerUnitTests { @@ -389,20 +375,16 @@ class R2dbcTransactionManagerUnitTests {
DefaultTransactionDefinition definition = new DefaultTransactionDefinition();
definition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
TransactionalOperator operator = TransactionalOperator.create(tm, definition);
TransactionalOperator operator = TransactionalOperator.create(tm, definition);
operator.execute(tx1 -> {
assertThat(tx1.isNewTransaction()).isTrue();
definition.setPropagationBehavior(TransactionDefinition.PROPAGATION_NEVER);
return operator.execute(tx2 -> {
fail("Should have thrown IllegalTransactionStateException");
return Mono.empty();
});
}).as(StepVerifier::create)
.verifyError(IllegalTransactionStateException.class);
}).as(StepVerifier::create).verifyError(IllegalTransactionStateException.class);
verify(connectionMock).rollbackTransaction();
verify(connectionMock).close();
@ -414,32 +396,49 @@ class R2dbcTransactionManagerUnitTests { @@ -414,32 +396,49 @@ class R2dbcTransactionManagerUnitTests {
DefaultTransactionDefinition definition = new DefaultTransactionDefinition();
definition.setPropagationBehavior(TransactionDefinition.PROPAGATION_SUPPORTS);
TransactionalOperator operator = TransactionalOperator.create(tm, definition);
TransactionalOperator operator = TransactionalOperator.create(tm, definition);
operator.execute(tx1 -> {
assertThat(tx1.isNewTransaction()).isFalse();
DefaultTransactionDefinition innerDef = new DefaultTransactionDefinition();
innerDef.setPropagationBehavior(
TransactionDefinition.PROPAGATION_REQUIRES_NEW);
innerDef.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
TransactionalOperator inner = TransactionalOperator.create(tm, innerDef);
return inner.execute(tx2 -> {
assertThat(tx2.isNewTransaction()).isTrue();
return Mono.empty();
});
}).as(StepVerifier::create)
.verifyComplete();
}).as(StepVerifier::create).verifyComplete();
verify(connectionMock).commitTransaction();
verify(connectionMock).close();
}
@Test
void testPropagationSupportsAndRequiresNewWithRollback() {
when(connectionMock.rollbackTransaction()).thenReturn(Mono.empty());
private static class TestTransactionSynchronization
implements TransactionSynchronization {
DefaultTransactionDefinition definition = new DefaultTransactionDefinition();
definition.setPropagationBehavior(TransactionDefinition.PROPAGATION_SUPPORTS);
TransactionalOperator operator = TransactionalOperator.create(tm, definition);
operator.execute(tx1 -> {
assertThat(tx1.isNewTransaction()).isFalse();
DefaultTransactionDefinition innerDef = new DefaultTransactionDefinition();
innerDef.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
TransactionalOperator inner = TransactionalOperator.create(tm, innerDef);
return inner.execute(tx2 -> {
assertThat(tx2.isNewTransaction()).isTrue();
tx2.setRollbackOnly();
return Mono.empty();
});
}).as(StepVerifier::create).verifyComplete();
verify(connectionMock).rollbackTransaction();
verify(connectionMock).close();
}
private static class TestTransactionSynchronization implements TransactionSynchronization {
private int status;
@ -512,7 +511,6 @@ class R2dbcTransactionManagerUnitTests { @@ -512,7 +511,6 @@ class R2dbcTransactionManagerUnitTests {
this.afterCompletionCalled = true;
assertThat(status).isEqualTo(this.status);
}
}
}

3
spring-tx/src/main/java/org/springframework/transaction/PlatformTransactionManager.java

@ -68,8 +68,7 @@ public interface PlatformTransactionManager extends TransactionManager { @@ -68,8 +68,7 @@ public interface PlatformTransactionManager extends TransactionManager {
* @see TransactionDefinition#getTimeout
* @see TransactionDefinition#isReadOnly
*/
TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
throws TransactionException;
TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException;
/**
* Commit the given transaction, with regard to its status. If the transaction

5
spring-tx/src/main/java/org/springframework/transaction/ReactiveTransaction.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2023 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.
@ -17,7 +17,7 @@ @@ -17,7 +17,7 @@
package org.springframework.transaction;
/**
* Representation of an ongoing reactive transaction.
* Representation of an ongoing {@link ReactiveTransactionManager} transaction.
* This is currently a marker interface extending {@link TransactionExecution}
* but may acquire further methods in a future revision.
*
@ -30,6 +30,7 @@ package org.springframework.transaction; @@ -30,6 +30,7 @@ package org.springframework.transaction;
* @since 5.2
* @see #setRollbackOnly()
* @see ReactiveTransactionManager#getReactiveTransaction
* @see org.springframework.transaction.reactive.TransactionCallback#doInTransaction
*/
public interface ReactiveTransaction extends TransactionExecution {

5
spring-tx/src/main/java/org/springframework/transaction/TransactionStatus.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2023 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.
@ -19,7 +19,8 @@ package org.springframework.transaction; @@ -19,7 +19,8 @@ package org.springframework.transaction;
import java.io.Flushable;
/**
* Representation of the status of a transaction.
* Representation of an ongoing {@link PlatformTransactionManager} transaction.
* Extends the common {@link TransactionExecution} interface.
*
* <p>Transactional code can use this to retrieve status information,
* and to programmatically request a rollback (instead of throwing

8
spring-tx/src/main/java/org/springframework/transaction/support/SmartTransactionObject.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2023 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.
@ -23,13 +23,13 @@ import java.io.Flushable; @@ -23,13 +23,13 @@ import java.io.Flushable;
* return an internal rollback-only marker, typically from another
* transaction that has participated and marked it as rollback-only.
*
* <p>Autodetected by DefaultTransactionStatus, to always return a
* current rollbackOnly flag even if not resulting from the current
* <p>Autodetected by {@link DefaultTransactionStatus} in order to always
* return a current rollbackOnly flag even if not resulting from the current
* TransactionStatus.
*
* @author Juergen Hoeller
* @since 1.1
* @see DefaultTransactionStatus#isRollbackOnly
* @see DefaultTransactionStatus#isGlobalRollbackOnly()
*/
public interface SmartTransactionObject extends Flushable {

Loading…
Cancel
Save