diff --git a/integration-tests/src/test/java/org/springframework/scheduling/annotation/ScheduledAndTransactionalAnnotationIntegrationTests.java b/integration-tests/src/test/java/org/springframework/scheduling/annotation/ScheduledAndTransactionalAnnotationIntegrationTests.java index 91a4c748e4e..b6f5102491a 100644 --- a/integration-tests/src/test/java/org/springframework/scheduling/annotation/ScheduledAndTransactionalAnnotationIntegrationTests.java +++ b/integration-tests/src/test/java/org/springframework/scheduling/annotation/ScheduledAndTransactionalAnnotationIntegrationTests.java @@ -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 { 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 { 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 { 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(); diff --git a/spring-aop/src/test/java/org/springframework/aop/framework/adapter/ThrowsAdviceInterceptorTests.java b/spring-aop/src/test/java/org/springframework/aop/framework/adapter/ThrowsAdviceInterceptorTests.java index ce5b5626e94..0b89db0d52d 100644 --- a/spring-aop/src/test/java/org/springframework/aop/framework/adapter/ThrowsAdviceInterceptorTests.java +++ b/spring-aop/src/test/java/org/springframework/aop/framework/adapter/ThrowsAdviceInterceptorTests.java @@ -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 { ConnectException ex = new ConnectException(""); MethodInvocation mi = mock(); 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 { ConnectException ex = new ConnectException(""); MethodInvocation mi = mock(); 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); } diff --git a/spring-context-support/src/test/java/org/springframework/scheduling/quartz/QuartzSupportTests.java b/spring-context-support/src/test/java/org/springframework/scheduling/quartz/QuartzSupportTests.java index 162fa930bc0..35ea29c98e6 100644 --- a/spring-context-support/src/test/java/org/springframework/scheduling/quartz/QuartzSupportTests.java +++ b/spring-context-support/src/test/java/org/springframework/scheduling/quartz/QuartzSupportTests.java @@ -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(); @@ -133,7 +133,7 @@ class QuartzSupportTests { } @Test - @SuppressWarnings({ "unchecked", "rawtypes" }) + @SuppressWarnings({"unchecked", "rawtypes"}) void jobDetailWithRunnableInsteadOfJob() { JobDetailImpl jobDetail = new JobDetailImpl(); assertThatIllegalArgumentException().isThrownBy(() -> @@ -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(); @@ -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(); @@ -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(); @@ -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(); diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ClassPathBeanDefinitionScanner.java b/spring-context/src/main/java/org/springframework/context/annotation/ClassPathBeanDefinitionScanner.java index 0b2f1d52a0b..be2de0956c9 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ClassPathBeanDefinitionScanner.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ClassPathBeanDefinitionScanner.java @@ -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 * the given existing bean definition. *
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 } diff --git a/spring-context/src/test/java/org/springframework/context/annotation/ClassPathBeanDefinitionScannerTests.java b/spring-context/src/test/java/org/springframework/context/annotation/ClassPathBeanDefinitionScannerTests.java index be96c741f6c..e669752de69 100644 --- a/spring-context/src/test/java/org/springframework/context/annotation/ClassPathBeanDefinitionScannerTests.java +++ b/spring-context/src/test/java/org/springframework/context/annotation/ClassPathBeanDefinitionScannerTests.java @@ -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 { 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()); diff --git a/spring-core/src/main/java/org/springframework/core/ReactiveAdapterRegistry.java b/spring-core/src/main/java/org/springframework/core/ReactiveAdapterRegistry.java index 50d8043294f..921cb0e99e9 100644 --- a/spring-core/src/main/java/org/springframework/core/ReactiveAdapterRegistry.java +++ b/spring-core/src/main/java/org/springframework/core/ReactiveAdapterRegistry.java @@ -24,8 +24,6 @@ import java.util.concurrent.CompletionStage; import java.util.concurrent.Flow; import java.util.function.Function; -import kotlinx.coroutines.CompletableDeferredKt; -import kotlinx.coroutines.Deferred; import org.reactivestreams.Publisher; import reactor.adapter.JdkFlowAdapter; import reactor.blockhound.BlockHound; @@ -38,13 +36,14 @@ import org.springframework.util.ClassUtils; import org.springframework.util.ConcurrentReferenceHashMap; /** - * 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. * - *
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}. + *
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. * * @author Rossen Stoyanchev * @author Sebastien Deleuze @@ -304,9 +303,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( diff --git a/spring-core/src/main/java/org/springframework/core/ReactiveTypeDescriptor.java b/spring-core/src/main/java/org/springframework/core/ReactiveTypeDescriptor.java index fe56c512b1c..5e37afabf09 100644 --- a/spring-core/src/main/java/org/springframework/core/ReactiveTypeDescriptor.java +++ b/spring-core/src/main/java/org/springframework/core/ReactiveTypeDescriptor.java @@ -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. @@ -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 { this.reactiveType = reactiveType; this.multiValue = multiValue; this.noValue = noValue; - this.emptyValueSupplier = emptySupplier; + this.emptySupplier = emptySupplier; this.deferred = deferred; } @@ -89,7 +89,7 @@ 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); } /** @@ -97,8 +97,8 @@ public final class ReactiveTypeDescriptor { *
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();
}
/**
diff --git a/spring-core/src/test/java/org/springframework/core/ReactiveAdapterRegistryTests.java b/spring-core/src/test/java/org/springframework/core/ReactiveAdapterRegistryTests.java
index 3a7a5ab2490..a969a482f63 100644
--- a/spring-core/src/test/java/org/springframework/core/ReactiveAdapterRegistryTests.java
+++ b/spring-core/src/test/java/org/springframework/core/ReactiveAdapterRegistryTests.java
@@ -20,6 +20,7 @@ import java.time.Duration;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Flow;
import io.smallrye.mutiny.Multi;
import io.smallrye.mutiny.Uni;
@@ -27,6 +28,7 @@ import kotlinx.coroutines.Deferred;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.reactivestreams.Publisher;
+import reactor.adapter.JdkFlowAdapter;
import reactor.core.CoreSubscriber;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@@ -112,6 +114,16 @@ class ReactiveAdapterRegistryTests {
assertThat(((Mono Transactional code can use this to retrieve status information,
* and to programmatically request a rollback (instead of throwing
diff --git a/spring-tx/src/main/java/org/springframework/transaction/support/SmartTransactionObject.java b/spring-tx/src/main/java/org/springframework/transaction/support/SmartTransactionObject.java
index ba5537c4439..3fa3e445a6d 100644
--- a/spring-tx/src/main/java/org/springframework/transaction/support/SmartTransactionObject.java
+++ b/spring-tx/src/main/java/org/springframework/transaction/support/SmartTransactionObject.java
@@ -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;
* return an internal rollback-only marker, typically from another
* transaction that has participated and marked it as rollback-only.
*
- * Autodetected by DefaultTransactionStatus, to always return a
- * current rollbackOnly flag even if not resulting from the current
+ * 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 {