Nikita
Nikita

Reputation: 1515

Inject RedeliveryPolicy to Spring boot ActiveMQ

I'm using spring-boot-starter-activemq with PoolFactory. Everything works great and it's easy to configure via application.yaml, but I cannot find correct way to inject my custom RedeliveryPolicy. So the question is how can I do that?

Upvotes: 3

Views: 7065

Answers (2)

Huy Leonis
Huy Leonis

Reputation: 154

I have tried the answer from @Iulian Rosca but it does not work for latest version of spring-boot-starter-activemq (I'm using version 2.2.0.RELEASE).

Spring provides many other implementation classes for ConnectionFactory, such as CachingConnectionFactory, JmsPooledConnectionFactory,... and they are not inherited to each others. Therefore, the above solution need to refactor with many class type.

I discover Spring provide a functional interface ActiveMQConnectionFactoryCustomizer, which will be injected when create ActiveMQ Connection Factory, whichever class no matter. It will customize connection factory class, you can set any properties, not only redelivery policy.

Here is my sample code:

@Configuration
public class RedeliveryCofiguration {

    @Bean
    public ActiveMQConnectionFactoryCustomizer configureRedeliveryPolicy() {
        return connectionFactory ->
        {
            RedeliveryPolicy redeliveryPolicy = new RedeliveryPolicy();
            // configure redelivery policy
            connectionFactory.setRedeliveryPolicy(redeliveryPolicy);
        };
    }
}

Upvotes: 10

Iulian Rosca
Iulian Rosca

Reputation: 1015

The re-delivery policy can be set on the connection factory. As the connection factory is configured automatically by spring boot, you can add a method to set it.

import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.activemq.RedeliveryPolicy;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class Foo
{
    @Bean
    public InitializingBean connectionFactory(ActiveMQConnectionFactory connectionFactory)
    {
        return () ->
        {
            RedeliveryPolicy redeliveryPolicy = new RedeliveryPolicy();
            // configure redelivery policy
            connectionFactory.setRedeliveryPolicy(redeliveryPolicy);
        };
    }
}

Update

As stated in one reply, the above solution works well when the following configuration is not set spring.activemq.pool.enabled = true.

When using a pooled connection factory, Spring Boot auto-configures a org.apache.activemq.pool.PooledConnectionFactory instead of org.apache.activemq.ActiveMQConnectionFactory. This configuration happens here: org.springframework.boot.autoconfigure.jms.activemq.ActiveMQConnectionFactoryConfiguration.PooledConnectionFactoryConfiguration#pooledJmsConnectionFactory in the following line:

PooledConnectionFactory pooledConnectionFactory = new PooledConnectionFactory(new ActiveMQConnectionFactoryFactory(properties,factoryCustomizers.getIfAvailable()).createConnectionFactory(ActiveMQConnectionFactory.class));

In order to make this work you have two options:

  1. Use the common parent for ActiveMQConnectionFactory and PooledConnectionFactory when injecting the bean, which is javax.jms.ConnectionFactory, and use instance-of and casting to set the redelivery policy.

        @Bean
        public InitializingBean connectionFactory(ConnectionFactory connectionFactory) {
    
            if (connectionFactory instanceof ActiveMQConnectionFactory) {
                return configureRedeliveryPolicy((ActiveMQConnectionFactory) connectionFactory);
    
            } else if (connectionFactory instanceof PooledConnectionFactory) {
                final PooledConnectionFactory pooledConnectionFactory = (PooledConnectionFactory) connectionFactory;
                if (pooledConnectionFactory.getConnectionFactory() instanceof ActiveMQConnectionFactory) {
                    return configureRedeliveryPolicy((ActiveMQConnectionFactory) pooledConnectionFactory.getConnectionFactory());
                }
            }
            // ...
        }
    
  2. Have two different custom initialization scenarios for each bean type, which is basically the same as 1, but you're leveraging ConditionalOnBean instead of checking for the connectionFactory class type.

    @Configuration
    public class Foo {
        @Bean
        @ConditionalOnBean(ActiveMQConnectionFactory.class)
        public InitializingBean connectionFactory(ActiveMQConnectionFactory connectionFactory) {
            return configureRedeliveryPolicy(connectionFactory);
        }
    
        @Bean
        @ConditionalOnBean(PooledConnectionFactory.class)
        public InitializingBean pooledConnectionFactory(PooledConnectionFactory connectionFactory) {
            if (connectionFactory.getConnectionFactory() instanceof ActiveMQConnectionFactory) {
                return configureRedeliveryPolicy((ActiveMQConnectionFactory) connectionFactory.getConnectionFactory());
            } else return () -> {
                // do something else
            };
        }
    
        private InitializingBean configureRedeliveryPolicy(ActiveMQConnectionFactory connectionFactory) {
            return () ->
            {
                RedeliveryPolicy redeliveryPolicy = new RedeliveryPolicy();
                // configure redelivery policy
                connectionFactory.setRedeliveryPolicy(redeliveryPolicy);
            };
        }
    }
    

Upvotes: 7

Related Questions