Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -191,3 +191,8 @@ NOTE: In the example above, the customization uses javadoc:org.springframework.b
Then you can use the factory in any javadoc:org.springframework.jms.annotation.JmsListener[format=annotation]-annotated method as follows:

include-code::custom/MyBean[]

Analogous to `DefaultJmsListenerContainerFactoryConfigurer`, Spring Boot also provides a javadoc:org.springframework.boot.jms.autoconfigure.SimpleJmsListenerContainerFactoryConfigurer[] that you can use to initialize a javadoc:org.springframework.jms.config.SimpleJmsListenerContainerFactory[] and apply the related settings that auto-configuration provides.

TIP: In contrast to javadoc:org.springframework.jms.listener.DefaultMessageListenerContainer[] that uses a pull-based mechanism (polling) to process messages, javadoc:org.springframework.jms.listener.SimpleMessageListenerContainer[] uses a push-based mechanism that's very close to the spirit of the standalone JMS specification.
To learn more about the differences between the two listener containers, consult their respective javadocs and {url-spring-framework-docs}/integration/jms/using.html#jms-mdp[Spring Framework reference documentation].
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
/*
* Copyright 2012-present 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.boot.jms.autoconfigure;

import io.micrometer.observation.ObservationRegistry;
import jakarta.jms.ConnectionFactory;
import jakarta.jms.ExceptionListener;
import org.jspecify.annotations.Nullable;

import org.springframework.boot.context.properties.PropertyMapper;
import org.springframework.boot.jms.autoconfigure.JmsProperties.Listener.Session;
import org.springframework.jms.config.AbstractJmsListenerContainerFactory;
import org.springframework.jms.support.converter.MessageConverter;
import org.springframework.jms.support.destination.DestinationResolver;
import org.springframework.util.Assert;

/**
* Configures {@link AbstractJmsListenerContainerFactory} with sensible defaults.
*
* @param <T> the connection factory type.
* @author Vedran Pavic
* @since 4.0.0
*/
public abstract class AbstractJmsListenerContainerFactoryConfigurer<T extends AbstractJmsListenerContainerFactory<?>> {

private @Nullable DestinationResolver destinationResolver;

private @Nullable MessageConverter messageConverter;

private @Nullable ExceptionListener exceptionListener;

private @Nullable ObservationRegistry observationRegistry;

private @Nullable JmsProperties jmsProperties;

/**
* Set the {@link DestinationResolver} to use or {@code null} if no destination
* resolver should be associated with the factory by default.
* @param destinationResolver the {@link DestinationResolver}
*/
void setDestinationResolver(@Nullable DestinationResolver destinationResolver) {
this.destinationResolver = destinationResolver;
}

/**
* Set the {@link MessageConverter} to use or {@code null} if the out-of-the-box
* converter should be used.
* @param messageConverter the {@link MessageConverter}
*/
void setMessageConverter(@Nullable MessageConverter messageConverter) {
this.messageConverter = messageConverter;
}

/**
* Set the {@link ExceptionListener} to use or {@code null} if no exception listener
* should be associated by default.
* @param exceptionListener the {@link ExceptionListener}
*/
void setExceptionListener(@Nullable ExceptionListener exceptionListener) {
this.exceptionListener = exceptionListener;
}

/**
* Set the {@link JmsProperties} to use.
* @param jmsProperties the {@link JmsProperties}
*/
void setJmsProperties(@Nullable JmsProperties jmsProperties) {
this.jmsProperties = jmsProperties;
}

/**
* Set the {@link ObservationRegistry} to use.
* @param observationRegistry the {@link ObservationRegistry}
*/
void setObservationRegistry(@Nullable ObservationRegistry observationRegistry) {
this.observationRegistry = observationRegistry;
}

/**
* Configure the specified jms listener container factory. The factory can be further
* tuned and default settings can be overridden.
* @param factory the {@link AbstractJmsListenerContainerFactory} instance to
* configure
* @param connectionFactory the {@link ConnectionFactory} to use
*/
public void configure(T factory, ConnectionFactory connectionFactory) {
Assert.notNull(factory, "'factory' must not be null");
Assert.notNull(connectionFactory, "'connectionFactory' must not be null");
Assert.state(this.jmsProperties != null, "'jmsProperties' must not be null");
JmsProperties.Listener listenerProperties = this.jmsProperties.getListener();
Session sessionProperties = listenerProperties.getSession();
factory.setConnectionFactory(connectionFactory);
PropertyMapper map = PropertyMapper.get();
map.from(this.jmsProperties::isPubSubDomain).to(factory::setPubSubDomain);
map.from(this.jmsProperties::isSubscriptionDurable).to(factory::setSubscriptionDurable);
map.from(this.jmsProperties::getClientId).to(factory::setClientId);
map.from(this.destinationResolver).to(factory::setDestinationResolver);
map.from(this.messageConverter).to(factory::setMessageConverter);
map.from(this.exceptionListener).to(factory::setExceptionListener);
map.from(sessionProperties.getAcknowledgeMode()::getMode).to(factory::setSessionAcknowledgeMode);
map.from(this.observationRegistry).to(factory::setObservationRegistry);
map.from(sessionProperties::getTransacted).to(factory::setSessionTransacted);
map.from(listenerProperties::isAutoStartup).to(factory::setAutoStartup);
configure(factory, connectionFactory, this.jmsProperties);
}

/**
* Configures the given {@code factory} using the given {@code connectionFactory} and
* {@code jmsProperties}.
* @param factory the {@link AbstractJmsListenerContainerFactory} instance to
* configure
* @param connectionFactory the {@link ConnectionFactory} to use
* @param jmsProperties the {@link JmsProperties} to use
*/
protected abstract void configure(T factory, ConnectionFactory connectionFactory, JmsProperties jmsProperties);

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,22 +18,17 @@

import java.time.Duration;

import io.micrometer.observation.ObservationRegistry;
import jakarta.jms.ConnectionFactory;
import jakarta.jms.ExceptionListener;
import org.jspecify.annotations.Nullable;

import org.springframework.boot.context.properties.PropertyMapper;
import org.springframework.boot.jms.autoconfigure.JmsProperties.Listener.Session;
import org.springframework.jms.config.DefaultJmsListenerContainerFactory;
import org.springframework.jms.support.converter.MessageConverter;
import org.springframework.jms.support.destination.DestinationResolver;
import org.springframework.transaction.jta.JtaTransactionManager;
import org.springframework.util.Assert;

/**
* Configure {@link DefaultJmsListenerContainerFactory} with sensible defaults tuned using
* configuration properties.
* Configures {@link DefaultJmsListenerContainerFactory} with sensible defaults tuned
* using configuration properties.
* <p>
* Can be injected into application code and used to define a custom
* {@code DefaultJmsListenerContainerFactory} whose configuration is based upon that
Expand All @@ -45,47 +40,11 @@
* @author Lasse Wulff
* @since 4.0.0
*/
public final class DefaultJmsListenerContainerFactoryConfigurer {

private @Nullable DestinationResolver destinationResolver;

private @Nullable MessageConverter messageConverter;

private @Nullable ExceptionListener exceptionListener;
public final class DefaultJmsListenerContainerFactoryConfigurer
extends AbstractJmsListenerContainerFactoryConfigurer<DefaultJmsListenerContainerFactory> {

private @Nullable JtaTransactionManager transactionManager;

private @Nullable JmsProperties jmsProperties;

private @Nullable ObservationRegistry observationRegistry;

/**
* Set the {@link DestinationResolver} to use or {@code null} if no destination
* resolver should be associated with the factory by default.
* @param destinationResolver the {@link DestinationResolver}
*/
void setDestinationResolver(@Nullable DestinationResolver destinationResolver) {
this.destinationResolver = destinationResolver;
}

/**
* Set the {@link MessageConverter} to use or {@code null} if the out-of-the-box
* converter should be used.
* @param messageConverter the {@link MessageConverter}
*/
void setMessageConverter(@Nullable MessageConverter messageConverter) {
this.messageConverter = messageConverter;
}

/**
* Set the {@link ExceptionListener} to use or {@code null} if no exception listener
* should be associated by default.
* @param exceptionListener the {@link ExceptionListener}
*/
void setExceptionListener(@Nullable ExceptionListener exceptionListener) {
this.exceptionListener = exceptionListener;
}

/**
* Set the {@link JtaTransactionManager} to use or {@code null} if the JTA support
* should not be used.
Expand All @@ -95,50 +54,16 @@ void setTransactionManager(@Nullable JtaTransactionManager transactionManager) {
this.transactionManager = transactionManager;
}

/**
* Set the {@link JmsProperties} to use.
* @param jmsProperties the {@link JmsProperties}
*/
void setJmsProperties(@Nullable JmsProperties jmsProperties) {
this.jmsProperties = jmsProperties;
}

/**
* Set the {@link ObservationRegistry} to use.
* @param observationRegistry the {@link ObservationRegistry}
*/
void setObservationRegistry(@Nullable ObservationRegistry observationRegistry) {
this.observationRegistry = observationRegistry;
}

/**
* Configure the specified jms listener container factory. The factory can be further
* tuned and default settings can be overridden.
* @param factory the {@link DefaultJmsListenerContainerFactory} instance to configure
* @param connectionFactory the {@link ConnectionFactory} to use
*/
public void configure(DefaultJmsListenerContainerFactory factory, ConnectionFactory connectionFactory) {
Assert.notNull(factory, "'factory' must not be null");
Assert.notNull(connectionFactory, "'connectionFactory' must not be null");
Assert.state(this.jmsProperties != null, "'jmsProperties' must not be null");
JmsProperties.Listener listenerProperties = this.jmsProperties.getListener();
Session sessionProperties = listenerProperties.getSession();
factory.setConnectionFactory(connectionFactory);
@Override
protected void configure(DefaultJmsListenerContainerFactory factory, ConnectionFactory connectionFactory,
JmsProperties jmsProperties) {
PropertyMapper map = PropertyMapper.get();
map.from(this.jmsProperties::isPubSubDomain).to(factory::setPubSubDomain);
map.from(this.jmsProperties::isSubscriptionDurable).to(factory::setSubscriptionDurable);
map.from(this.jmsProperties::getClientId).to(factory::setClientId);
JmsProperties.Listener listenerProperties = jmsProperties.getListener();
Session sessionProperties = listenerProperties.getSession();
map.from(this.transactionManager).to(factory::setTransactionManager);
map.from(this.destinationResolver).to(factory::setDestinationResolver);
map.from(this.messageConverter).to(factory::setMessageConverter);
map.from(this.exceptionListener).to(factory::setExceptionListener);
map.from(sessionProperties.getAcknowledgeMode()::getMode).to(factory::setSessionAcknowledgeMode);
if (this.transactionManager == null && sessionProperties.getTransacted() == null) {
factory.setSessionTransacted(true);
}
map.from(this.observationRegistry).to(factory::setObservationRegistry);
map.from(sessionProperties::getTransacted).to(factory::setSessionTransacted);
map.from(listenerProperties::isAutoStartup).to(factory::setAutoStartup);
map.from(listenerProperties::formatConcurrency).to(factory::setConcurrency);
map.from(listenerProperties::getReceiveTimeout).as(Duration::toMillis).to(factory::setReceiveTimeout);
map.from(listenerProperties::getMaxMessagesPerTask).to(factory::setMaxMessagesPerTask);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
* @author Phillip Webb
* @author Stephane Nicoll
* @author Eddú Meléndez
* @author Vedran Pavic
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(EnableJms.class)
Expand Down Expand Up @@ -73,8 +74,7 @@ class JmsAnnotationDrivenConfiguration {

@Bean
@ConditionalOnMissingBean
@SuppressWarnings("removal")
DefaultJmsListenerContainerFactoryConfigurer jmsListenerContainerFactoryConfigurer() {
DefaultJmsListenerContainerFactoryConfigurer defaultJmsListenerContainerFactoryConfigurer() {
DefaultJmsListenerContainerFactoryConfigurer configurer = new DefaultJmsListenerContainerFactoryConfigurer();
configurer.setDestinationResolver(this.destinationResolver.getIfUnique());
configurer.setTransactionManager(this.transactionManager.getIfUnique());
Expand All @@ -85,6 +85,18 @@ DefaultJmsListenerContainerFactoryConfigurer jmsListenerContainerFactoryConfigur
return configurer;
}

@Bean
@ConditionalOnMissingBean
SimpleJmsListenerContainerFactoryConfigurer simpleJmsListenerContainerFactoryConfigurer() {
SimpleJmsListenerContainerFactoryConfigurer configurer = new SimpleJmsListenerContainerFactoryConfigurer();
configurer.setDestinationResolver(this.destinationResolver.getIfUnique());
configurer.setMessageConverter(this.messageConverter.getIfUnique());
configurer.setExceptionListener(this.exceptionListener.getIfUnique());
configurer.setObservationRegistry(this.observationRegistry.getIfUnique());
configurer.setJmsProperties(this.properties);
return configurer;
}

@Bean
@ConditionalOnSingleCandidate(ConnectionFactory.class)
@ConditionalOnMissingBean(name = "jmsListenerContainerFactory")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright 2012-present 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.boot.jms.autoconfigure;

import jakarta.jms.ConnectionFactory;

import org.springframework.jms.config.SimpleJmsListenerContainerFactory;

/**
* Configures {@link SimpleJmsListenerContainerFactory} with sensible defaults.
*
* @author Vedran Pavic
* @since 4.0.0
*/
public final class SimpleJmsListenerContainerFactoryConfigurer
extends AbstractJmsListenerContainerFactoryConfigurer<SimpleJmsListenerContainerFactory> {

@Override
protected void configure(SimpleJmsListenerContainerFactory factory, ConnectionFactory connectionFactory,
JmsProperties jmsProperties) {
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ void testNoConnectionFactoryJmsConfiguration() {
.doesNotHaveBean(JmsMessagingTemplate.class)
.doesNotHaveBean(JmsClient.class)
.doesNotHaveBean(DefaultJmsListenerContainerFactoryConfigurer.class)
.doesNotHaveBean(SimpleJmsListenerContainerFactoryConfigurer.class)
.doesNotHaveBean(DefaultJmsListenerContainerFactory.class));
}

Expand Down Expand Up @@ -495,9 +496,10 @@ JmsMessagingTemplate jmsMessagingTemplate(JmsTemplate jmsTemplate) {
static class TestConfiguration6 {

@Bean
JmsListenerContainerFactory<?> jmsListenerContainerFactory(ConnectionFactory connectionFactory) {
JmsListenerContainerFactory<?> jmsListenerContainerFactory(
SimpleJmsListenerContainerFactoryConfigurer configurer, ConnectionFactory connectionFactory) {
SimpleJmsListenerContainerFactory factory = new SimpleJmsListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);
configurer.configure(factory, connectionFactory);
return factory;
}

Expand Down