Defensively unwrap CacheConnectionFactory
This commit refines the optimization introduced in gh-39816 to only unwrap our own caching connection factory. The more advanced unwrap algorithm is still available, but opt-in only. Unwrapping more aggressively may break use cases where the wrapped ConnectionFactory is required, i.e. for transactional purposes. Closes gh-43277
This commit is contained in:
parent
6029b01d3d
commit
d8c41c2583
|
@ -90,7 +90,7 @@ class JmsAnnotationDrivenConfiguration {
|
|||
DefaultJmsListenerContainerFactory jmsListenerContainerFactory(
|
||||
DefaultJmsListenerContainerFactoryConfigurer configurer, ConnectionFactory connectionFactory) {
|
||||
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
|
||||
configurer.configure(factory, ConnectionFactoryUnwrapper.unwrap(connectionFactory));
|
||||
configurer.configure(factory, ConnectionFactoryUnwrapper.unwrapCaching(connectionFactory));
|
||||
return factory;
|
||||
}
|
||||
|
||||
|
|
|
@ -165,6 +165,8 @@ In most scenarios, message listener containers should be configured against the
|
|||
This way each listener container has its own connection and this gives full responsibility to it in terms of local recovery.
|
||||
The auto-configuration uses javadoc:org.springframework.boot.jms.ConnectionFactoryUnwrapper[] to unwrap the native connection factory from the auto-configured one.
|
||||
|
||||
NOTE: The auto-configuration only unwraps `CachedConnectionFactory`.
|
||||
|
||||
By default, the default factory is transactional.
|
||||
If you run in an infrastructure where a javadoc:org.springframework.transaction.jta.JtaTransactionManager[] is present, it is associated to the listener container by default.
|
||||
If not, the `sessionTransacted` flag is enabled.
|
||||
|
|
|
@ -31,7 +31,7 @@ public class MyJmsConfiguration {
|
|||
public DefaultJmsListenerContainerFactory jmsListenerContainerFactory(ConnectionFactory connectionFactory,
|
||||
DefaultJmsListenerContainerFactoryConfigurer configurer) {
|
||||
DefaultJmsListenerContainerFactory listenerFactory = new DefaultJmsListenerContainerFactory();
|
||||
configurer.configure(listenerFactory, ConnectionFactoryUnwrapper.unwrap(connectionFactory));
|
||||
configurer.configure(listenerFactory, ConnectionFactoryUnwrapper.unwrapCaching(connectionFactory));
|
||||
listenerFactory.setTransactionManager(null);
|
||||
listenerFactory.setSessionTransacted(false);
|
||||
return listenerFactory;
|
||||
|
|
|
@ -31,7 +31,7 @@ public class MyJmsConfiguration {
|
|||
public DefaultJmsListenerContainerFactory myFactory(DefaultJmsListenerContainerFactoryConfigurer configurer,
|
||||
ConnectionFactory connectionFactory) {
|
||||
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
|
||||
configurer.configure(factory, ConnectionFactoryUnwrapper.unwrap(connectionFactory));
|
||||
configurer.configure(factory, ConnectionFactoryUnwrapper.unwrapCaching(connectionFactory));
|
||||
factory.setMessageConverter(new MyMessageConverter());
|
||||
return factory;
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ class MyJmsConfiguration {
|
|||
fun jmsListenerContainerFactory(connectionFactory: ConnectionFactory?,
|
||||
configurer: DefaultJmsListenerContainerFactoryConfigurer): DefaultJmsListenerContainerFactory {
|
||||
val listenerFactory = DefaultJmsListenerContainerFactory()
|
||||
configurer.configure(listenerFactory, ConnectionFactoryUnwrapper.unwrap(connectionFactory))
|
||||
configurer.configure(listenerFactory, ConnectionFactoryUnwrapper.unwrapCaching(connectionFactory))
|
||||
listenerFactory.setTransactionManager(null)
|
||||
listenerFactory.setSessionTransacted(false)
|
||||
return listenerFactory
|
||||
|
|
|
@ -30,7 +30,7 @@ class MyJmsConfiguration {
|
|||
fun myFactory(configurer: DefaultJmsListenerContainerFactoryConfigurer,
|
||||
connectionFactory: ConnectionFactory): DefaultJmsListenerContainerFactory {
|
||||
val factory = DefaultJmsListenerContainerFactory()
|
||||
configurer.configure(factory, ConnectionFactoryUnwrapper.unwrap(connectionFactory))
|
||||
configurer.configure(factory, ConnectionFactoryUnwrapper.unwrapCaching(connectionFactory))
|
||||
factory.setMessageConverter(MyMessageConverter())
|
||||
return factory
|
||||
}
|
||||
|
|
|
@ -33,6 +33,23 @@ public final class ConnectionFactoryUnwrapper {
|
|||
private ConnectionFactoryUnwrapper() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the native {@link ConnectionFactory} by unwrapping ot from a
|
||||
* {@link CachingConnectionFactory}. Return the given {@link ConnectionFactory} if no
|
||||
* {@link CachingConnectionFactory} wrapper has been detected.
|
||||
* @param connectionFactory a connection factory
|
||||
* @return the native connection factory that a {@link CachingConnectionFactory}
|
||||
* wraps, if any
|
||||
* @since 3.4.1
|
||||
*/
|
||||
public static ConnectionFactory unwrapCaching(ConnectionFactory connectionFactory) {
|
||||
if (connectionFactory instanceof CachingConnectionFactory cachingConnectionFactory) {
|
||||
ConnectionFactory unwrapedConnectionFactory = cachingConnectionFactory.getTargetConnectionFactory();
|
||||
return (unwrapedConnectionFactory != null) ? unwrapCaching(unwrapedConnectionFactory) : connectionFactory;
|
||||
}
|
||||
return connectionFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the native {@link ConnectionFactory} by unwrapping it from a cache or pool
|
||||
* connection factory. Return the given {@link ConnectionFactory} if no caching
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
package org.springframework.boot.jms;
|
||||
|
||||
import jakarta.jms.ConnectionFactory;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.messaginghub.pooled.jms.JmsPoolConnectionFactory;
|
||||
|
||||
|
@ -35,59 +36,110 @@ import static org.mockito.Mockito.mock;
|
|||
*/
|
||||
class ConnectionFactoryUnwrapperTests {
|
||||
|
||||
@Test
|
||||
void unwrapWithSingleConnectionFactory() {
|
||||
ConnectionFactory connectionFactory = new SingleConnectionFactory();
|
||||
assertThat(ConnectionFactoryUnwrapper.unwrap(connectionFactory)).isSameAs(connectionFactory);
|
||||
@Nested
|
||||
class UnwrapCaching {
|
||||
|
||||
@Test
|
||||
void unwrapWithSingleConnectionFactory() {
|
||||
ConnectionFactory connectionFactory = new SingleConnectionFactory();
|
||||
assertThat(unwrapCaching(connectionFactory)).isSameAs(connectionFactory);
|
||||
}
|
||||
|
||||
@Test
|
||||
void unwrapWithConnectionFactory() {
|
||||
ConnectionFactory connectionFactory = mock(ConnectionFactory.class);
|
||||
assertThat(unwrapCaching(connectionFactory)).isSameAs(connectionFactory);
|
||||
}
|
||||
|
||||
@Test
|
||||
void unwrapWithCachingConnectionFactory() {
|
||||
ConnectionFactory connectionFactory = mock(ConnectionFactory.class);
|
||||
assertThat(unwrapCaching(new CachingConnectionFactory(connectionFactory))).isSameAs(connectionFactory);
|
||||
}
|
||||
|
||||
@Test
|
||||
void unwrapWithNestedCachingConnectionFactories() {
|
||||
ConnectionFactory connectionFactory = mock(ConnectionFactory.class);
|
||||
CachingConnectionFactory firstCachingConnectionFactory = new CachingConnectionFactory(connectionFactory);
|
||||
CachingConnectionFactory secondCachingConnectionFactory = new CachingConnectionFactory(
|
||||
firstCachingConnectionFactory);
|
||||
assertThat(unwrapCaching(secondCachingConnectionFactory)).isSameAs(connectionFactory);
|
||||
}
|
||||
|
||||
@Test
|
||||
void unwrapWithJmsPoolConnectionFactory() {
|
||||
ConnectionFactory connectionFactory = mock(ConnectionFactory.class);
|
||||
JmsPoolConnectionFactory poolConnectionFactory = new JmsPoolConnectionFactory();
|
||||
poolConnectionFactory.setConnectionFactory(connectionFactory);
|
||||
assertThat(unwrapCaching(poolConnectionFactory)).isSameAs(poolConnectionFactory);
|
||||
}
|
||||
|
||||
private ConnectionFactory unwrapCaching(ConnectionFactory connectionFactory) {
|
||||
return ConnectionFactoryUnwrapper.unwrapCaching(connectionFactory);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void unwrapWithConnectionFactory() {
|
||||
ConnectionFactory connectionFactory = mock(ConnectionFactory.class);
|
||||
assertThat(ConnectionFactoryUnwrapper.unwrap(connectionFactory)).isSameAs(connectionFactory);
|
||||
}
|
||||
@Nested
|
||||
class Unwrap {
|
||||
|
||||
@Test
|
||||
void unwrapWithCachingConnectionFactory() {
|
||||
ConnectionFactory connectionFactory = mock(ConnectionFactory.class);
|
||||
assertThat(ConnectionFactoryUnwrapper.unwrap(new CachingConnectionFactory(connectionFactory)))
|
||||
.isSameAs(connectionFactory);
|
||||
}
|
||||
@Test
|
||||
void unwrapWithSingleConnectionFactory() {
|
||||
ConnectionFactory connectionFactory = new SingleConnectionFactory();
|
||||
assertThat(unwrap(connectionFactory)).isSameAs(connectionFactory);
|
||||
}
|
||||
|
||||
@Test
|
||||
void unwrapWithNestedCachingConnectionFactories() {
|
||||
ConnectionFactory connectionFactory = mock(ConnectionFactory.class);
|
||||
CachingConnectionFactory firstCachingConnectionFactory = new CachingConnectionFactory(connectionFactory);
|
||||
CachingConnectionFactory secondCachingConnectionFactory = new CachingConnectionFactory(
|
||||
firstCachingConnectionFactory);
|
||||
assertThat(ConnectionFactoryUnwrapper.unwrap(secondCachingConnectionFactory)).isSameAs(connectionFactory);
|
||||
}
|
||||
@Test
|
||||
void unwrapWithConnectionFactory() {
|
||||
ConnectionFactory connectionFactory = mock(ConnectionFactory.class);
|
||||
assertThat(unwrap(connectionFactory)).isSameAs(connectionFactory);
|
||||
}
|
||||
|
||||
@Test
|
||||
void unwrapWithJmsPoolConnectionFactory() {
|
||||
ConnectionFactory connectionFactory = mock(ConnectionFactory.class);
|
||||
JmsPoolConnectionFactory poolConnectionFactory = new JmsPoolConnectionFactory();
|
||||
poolConnectionFactory.setConnectionFactory(connectionFactory);
|
||||
assertThat(ConnectionFactoryUnwrapper.unwrap(poolConnectionFactory)).isSameAs(connectionFactory);
|
||||
}
|
||||
@Test
|
||||
void unwrapWithCachingConnectionFactory() {
|
||||
ConnectionFactory connectionFactory = mock(ConnectionFactory.class);
|
||||
assertThat(unwrap(new CachingConnectionFactory(connectionFactory))).isSameAs(connectionFactory);
|
||||
}
|
||||
|
||||
@Test
|
||||
void unwrapWithNestedJmsPoolConnectionFactories() {
|
||||
ConnectionFactory connectionFactory = mock(ConnectionFactory.class);
|
||||
JmsPoolConnectionFactory firstPooledConnectionFactory = new JmsPoolConnectionFactory();
|
||||
firstPooledConnectionFactory.setConnectionFactory(connectionFactory);
|
||||
JmsPoolConnectionFactory secondPooledConnectionFactory = new JmsPoolConnectionFactory();
|
||||
secondPooledConnectionFactory.setConnectionFactory(firstPooledConnectionFactory);
|
||||
assertThat(ConnectionFactoryUnwrapper.unwrap(secondPooledConnectionFactory)).isSameAs(connectionFactory);
|
||||
}
|
||||
@Test
|
||||
void unwrapWithNestedCachingConnectionFactories() {
|
||||
ConnectionFactory connectionFactory = mock(ConnectionFactory.class);
|
||||
CachingConnectionFactory firstCachingConnectionFactory = new CachingConnectionFactory(connectionFactory);
|
||||
CachingConnectionFactory secondCachingConnectionFactory = new CachingConnectionFactory(
|
||||
firstCachingConnectionFactory);
|
||||
assertThat(unwrap(secondCachingConnectionFactory)).isSameAs(connectionFactory);
|
||||
}
|
||||
|
||||
@Test
|
||||
void unwrapWithJmsPoolConnectionFactory() {
|
||||
ConnectionFactory connectionFactory = mock(ConnectionFactory.class);
|
||||
JmsPoolConnectionFactory poolConnectionFactory = new JmsPoolConnectionFactory();
|
||||
poolConnectionFactory.setConnectionFactory(connectionFactory);
|
||||
assertThat(unwrap(poolConnectionFactory)).isSameAs(connectionFactory);
|
||||
}
|
||||
|
||||
@Test
|
||||
void unwrapWithNestedJmsPoolConnectionFactories() {
|
||||
ConnectionFactory connectionFactory = mock(ConnectionFactory.class);
|
||||
JmsPoolConnectionFactory firstPooledConnectionFactory = new JmsPoolConnectionFactory();
|
||||
firstPooledConnectionFactory.setConnectionFactory(connectionFactory);
|
||||
JmsPoolConnectionFactory secondPooledConnectionFactory = new JmsPoolConnectionFactory();
|
||||
secondPooledConnectionFactory.setConnectionFactory(firstPooledConnectionFactory);
|
||||
assertThat(unwrap(secondPooledConnectionFactory)).isSameAs(connectionFactory);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ClassPathExclusions("pooled-jms-*")
|
||||
void unwrapWithoutJmsPoolOnClasspath() {
|
||||
assertThat(ClassUtils.isPresent("org.messaginghub.pooled.jms.JmsPoolConnectionFactory", null)).isFalse();
|
||||
ConnectionFactory connectionFactory = mock(ConnectionFactory.class);
|
||||
assertThat(unwrap(new CachingConnectionFactory(connectionFactory))).isSameAs(connectionFactory);
|
||||
}
|
||||
|
||||
private ConnectionFactory unwrap(ConnectionFactory connectionFactory) {
|
||||
return ConnectionFactoryUnwrapper.unwrap(connectionFactory);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ClassPathExclusions("pooled-jms-*")
|
||||
void unwrapWithoutJmsPoolOnClasspath() {
|
||||
assertThat(ClassUtils.isPresent("org.messaginghub.pooled.jms.JmsPoolConnectionFactory", null)).isFalse();
|
||||
ConnectionFactory connectionFactory = mock(ConnectionFactory.class);
|
||||
assertThat(ConnectionFactoryUnwrapper.unwrap(new CachingConnectionFactory(connectionFactory)))
|
||||
.isSameAs(connectionFactory);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue