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:
Stéphane Nicoll
2024-11-26 19:56:32 +01:00
parent 6029b01d3d
commit d8c41c2583
8 changed files with 122 additions and 51 deletions

View File

@@ -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

View File

@@ -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);
}
}