diff --git a/spring-context/src/main/java/org/springframework/scheduling/concurrent/ExecutorConfigurationSupport.java b/spring-context/src/main/java/org/springframework/scheduling/concurrent/ExecutorConfigurationSupport.java
index 17e76d0190..55ca08ed9b 100644
--- a/spring-context/src/main/java/org/springframework/scheduling/concurrent/ExecutorConfigurationSupport.java
+++ b/spring-context/src/main/java/org/springframework/scheduling/concurrent/ExecutorConfigurationSupport.java
@@ -36,7 +36,10 @@ import org.springframework.lang.Nullable;
* Base class for setting up a {@link java.util.concurrent.ExecutorService}
* (typically a {@link java.util.concurrent.ThreadPoolExecutor} or
* {@link java.util.concurrent.ScheduledThreadPoolExecutor}).
- * Defines common configuration settings and common lifecycle handling.
+ *
+ *
Defines common configuration settings and common lifecycle handling,
+ * inheriting thread customization options (name, priority, etc) from
+ * {@link org.springframework.util.CustomizableThreadCreator}.
*
* @author Juergen Hoeller
* @since 3.0
@@ -199,8 +202,7 @@ public abstract class ExecutorConfigurationSupport extends CustomizableThreadFac
/**
- * Calls {@code shutdown} when the BeanFactory destroys
- * the task executor instance.
+ * Calls {@code shutdown} when the BeanFactory destroys the executor instance.
* @see #shutdown()
*/
@Override
diff --git a/spring-context/src/test/java/org/springframework/context/support/GenericApplicationContextTests.java b/spring-context/src/test/java/org/springframework/context/support/GenericApplicationContextTests.java
index f90a428128..dbcbb90cff 100644
--- a/spring-context/src/test/java/org/springframework/context/support/GenericApplicationContextTests.java
+++ b/spring-context/src/test/java/org/springframework/context/support/GenericApplicationContextTests.java
@@ -76,11 +76,13 @@ class GenericApplicationContextTests {
private final GenericApplicationContext context = new GenericApplicationContext();
+
@AfterEach
void closeContext() {
context.close();
}
+
@Test
void getBeanForClass() {
context.registerBeanDefinition("testBean", new RootBeanDefinition(String.class));
@@ -91,7 +93,7 @@ class GenericApplicationContextTests {
assertThat(context.getBean(CharSequence.class)).isSameAs(context.getBean("testBean"));
assertThatExceptionOfType(NoUniqueBeanDefinitionException.class)
- .isThrownBy(() -> context.getBean(Object.class));
+ .isThrownBy(() -> context.getBean(Object.class));
}
@Test
@@ -509,6 +511,7 @@ class GenericApplicationContextTests {
context.close();
}
+
private MergedBeanDefinitionPostProcessor registerMockMergedBeanDefinitionPostProcessor(GenericApplicationContext context) {
MergedBeanDefinitionPostProcessor bpp = mock();
context.registerBeanDefinition("bpp", BeanDefinitionBuilder.rootBeanDefinition(
@@ -563,9 +566,9 @@ class GenericApplicationContextTests {
public void setCounter(Integer counter) {
this.counter = counter;
}
-
}
+
static class TestAotFactoryBean extends AbstractFactoryBean {
TestAotFactoryBean() {
@@ -584,6 +587,7 @@ class GenericApplicationContextTests {
}
}
+
static class PingPongProtocolResolver implements ProtocolResolver {
@Override
diff --git a/spring-jms/src/test/java/org/springframework/jms/listener/DefaultMessageListenerContainerTests.java b/spring-jms/src/test/java/org/springframework/jms/listener/DefaultMessageListenerContainerTests.java
index 4315cce0a4..837d6ea69b 100644
--- a/spring-jms/src/test/java/org/springframework/jms/listener/DefaultMessageListenerContainerTests.java
+++ b/spring-jms/src/test/java/org/springframework/jms/listener/DefaultMessageListenerContainerTests.java
@@ -29,6 +29,7 @@ import org.mockito.stubbing.Answer;
import org.springframework.util.backoff.BackOff;
import org.springframework.util.backoff.BackOffExecution;
+import org.springframework.util.backoff.FixedBackOff;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
@@ -38,6 +39,7 @@ import static org.mockito.Mockito.verify;
/**
* @author Stephane Nicoll
+ * @author Juergen Hoeller
*/
public class DefaultMessageListenerContainerTests {
@@ -58,6 +60,8 @@ public class DefaultMessageListenerContainerTests {
assertThat(container.isRunning()).isFalse();
verify(backOff).start();
verify(execution).nextBackOff();
+
+ container.destroy();
}
@Test
@@ -75,6 +79,8 @@ public class DefaultMessageListenerContainerTests {
assertThat(container.isRunning()).isFalse();
verify(backOff).start();
verify(execution, times(2)).nextBackOff();
+
+ container.destroy();
}
@Test
@@ -92,27 +98,65 @@ public class DefaultMessageListenerContainerTests {
assertThat(container.isRunning()).isTrue();
verify(backOff).start();
verify(execution, times(1)).nextBackOff(); // only on attempt as the second one lead to a recovery
+
+ container.destroy();
}
@Test
- public void runnableIsInvokedEvenIfContainerIsNotRunning() throws InterruptedException {
+ public void stopAndRestart() {
+ DefaultMessageListenerContainer container = createRunningContainer();
+ container.stop();
+
+ container.start();
+ container.destroy();
+ }
+
+ @Test
+ public void stopWithCallbackAndRestart() throws InterruptedException {
+ DefaultMessageListenerContainer container = createRunningContainer();
+
+ TestRunnable stopCallback = new TestRunnable();
+ container.stop(stopCallback);
+ stopCallback.waitForCompletion();
+
+ container.start();
+ container.destroy();
+ }
+
+ @Test
+ public void stopCallbackIsInvokedEvenIfContainerIsNotRunning() throws InterruptedException {
DefaultMessageListenerContainer container = createRunningContainer();
container.stop();
// container is stopped but should nevertheless invoke the runnable argument
- TestRunnable runnable2 = new TestRunnable();
- container.stop(runnable2);
- runnable2.waitForCompletion();
+ TestRunnable stopCallback = new TestRunnable();
+ container.stop(stopCallback);
+ stopCallback.waitForCompletion();
+
+ container.destroy();
}
private DefaultMessageListenerContainer createRunningContainer() {
DefaultMessageListenerContainer container = createContainer(createSuccessfulConnectionFactory());
+ container.setCacheLevel(DefaultMessageListenerContainer.CACHE_CONNECTION);
+ container.setBackOff(new FixedBackOff(100, 1));
container.afterPropertiesSet();
container.start();
return container;
}
+ private ConnectionFactory createSuccessfulConnectionFactory() {
+ try {
+ ConnectionFactory connectionFactory = mock();
+ given(connectionFactory.createConnection()).willReturn(mock());
+ return connectionFactory;
+ }
+ catch (JMSException ex) {
+ throw new IllegalStateException(ex); // never happen
+ }
+ }
+
private DefaultMessageListenerContainer createContainer(ConnectionFactory connectionFactory) {
Destination destination = new Destination() {};
@@ -159,17 +203,6 @@ public class DefaultMessageListenerContainerTests {
}
}
- private ConnectionFactory createSuccessfulConnectionFactory() {
- try {
- ConnectionFactory connectionFactory = mock();
- given(connectionFactory.createConnection()).willReturn(mock());
- return connectionFactory;
- }
- catch (JMSException ex) {
- throw new IllegalStateException(ex); // never happen
- }
- }
-
private static class TestRunnable implements Runnable {
@@ -181,7 +214,7 @@ public class DefaultMessageListenerContainerTests {
}
public void waitForCompletion() throws InterruptedException {
- this.countDownLatch.await(2, TimeUnit.SECONDS);
+ this.countDownLatch.await(1, TimeUnit.SECONDS);
assertThat(this.countDownLatch.getCount()).as("callback was not invoked").isEqualTo(0);
}
}
diff --git a/spring-tx/src/main/java/org/springframework/transaction/interceptor/TransactionAspectSupport.java b/spring-tx/src/main/java/org/springframework/transaction/interceptor/TransactionAspectSupport.java
index c0a1805a60..e1e95bae23 100644
--- a/spring-tx/src/main/java/org/springframework/transaction/interceptor/TransactionAspectSupport.java
+++ b/spring-tx/src/main/java/org/springframework/transaction/interceptor/TransactionAspectSupport.java
@@ -1083,10 +1083,11 @@ public abstract class TransactionAspectSupport implements BeanFactoryAware, Init
* @param ex the throwable to try to unwrap
*/
private Throwable unwrapIfResourceCleanupFailure(Throwable ex) {
- if (ex instanceof RuntimeException &&
- ex.getCause() != null &&
- ex.getMessage().startsWith("Async resource cleanup failed")) {
- return ex.getCause();
+ if (ex instanceof RuntimeException && ex.getCause() != null) {
+ String msg = ex.getMessage();
+ if (msg != null && msg.startsWith("Async resource cleanup failed")) {
+ return ex.getCause();
+ }
}
return ex;
}