Восстановить JMS ConnectionFactory из JNDI с помощью автоматической настройки загрузки Spring

#spring #spring-boot #jms #jndi #spring-jms

#spring #spring-boot #jms #jndi #spring-jms

Вопрос:

Я хочу использовать автоконфигурацию spring boot для JMS для подключения к удаленному JNDI и получить ConnectionFactory на основе его имени, заполненного через свойство spring.jms.jndi-name в файле application.properties.

Я заметил, что для этого автоконфигурация spring boot полагается на класс JndiConnectionFactoryAutoConfiguration, а этот класс, в свою очередь, вызовет класс JndiTemplate для выполнения поиска. Проблема в том, что значение атрибута среды класса JndiTemplate равно null, поэтому мы не можем создать intialContext.

На самом деле, я заметил, что класс JndiTemplate создается с помощью конструктора без аргументов при запуске приложения и перед загрузкой конфигурации, определенной в классе JndiConnectionFactoryAutoConfiguration.

Мой вопрос: как я могу создать экземпляр JndiTemplate, указав список свойств (Context.INITIAL_CONTEXT_FACTORY, контекст.PROVIDER_URL..)? зная, что у JmsTemplate есть конструктор, который принимает объект свойств.

Просто для информации: мое приложение представляет собой простой jar, который в данный момент не запускается на сервере.

Комментарии:

1. разве вчера здесь не было ответа 🙂

2. Я публикую ответ;)

Ответ №1:

Для тех, кого интересует ответ, вы должны использовать параметры виртуальной машины для передачи требуемых свойств JNDI.

Вот пример, который работает с ActiveMQ:

Параметры виртуальной машины:

 -Djava.naming.provider.url=tcp://hostname:61616

-Djava.naming.factory.initial=org.apache.activemq.jndi.ActiveMQInitialContextFactory
  

И файл свойств spring (application.properties) должен содержать имя JNDI фабрики соединений:

 spring.jms.jndi-name=ConnectionFactory
  

Гораздо лучше, вы можете использовать конфигурацию, чтобы настроить свою фабрику соединений из JNDI. В моем проекте мы завершили созданием нашего jms starter, который мы можем использовать во всех микросервисах.

Класс свойств:

 import lombok.*;
import org.springframework.boot.context.properties.ConfigurationProperties;


@Getter
@Setter
@ToString
@NoArgsConstructor
@EqualsAndHashCode
@ConfigurationProperties( prefix = "custom.jms" )
public class CustomJmsProperties {
    private String jndiName;
    private String contextFactoryClass;
    private String providerUrl;
    private String username;
    private String password;
}
  

Класс конфигурации:

 import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.jms.JndiConnectionFactoryAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jms.connection.UserCredentialsConnectionFactoryAdapter;
import org.springframework.jndi.JndiLocatorDelegate;

import javax.jms.ConnectionFactory;
import javax.naming.Context;
import javax.naming.NamingException;
import java.util.Properties;

@Configuration
@ConditionalOnProperty( "custom.jms.jndi-name" )
@ConditionalOnMissingBean( ConnectionFactory.class )
@EnableConfigurationProperties( { CustomJmsProperties.class } )
@AutoConfigureAfter( { JndiConnectionFactoryAutoConfiguration.class } )
public class CustomJndiConnectionFactoryAutoConfiguration {

    @Bean
    public ConnectionFactory connectionFactory( CustomJmsProperties customJmsProperties ) throws NamingException {
        ConnectionFactory connectionFactory = lookupForConnectionFactory( customJmsProperties );
        return getEnhancedUserCredentialsConnectionFactory( customJmsProperties, connectionFactory );
    }

    private ConnectionFactory lookupForConnectionFactory( final CustomJmsProperties customJmsProperties ) throws NamingException {
        JndiLocatorDelegate jndiLocatorDelegate = new JndiLocatorDelegate();
        Properties jndiProperties = getJndiProperties( customJmsProperties );
        jndiLocatorDelegate.setJndiEnvironment( jndiProperties );
        return jndiLocatorDelegate.lookup( customJmsProperties.getJndiName(), ConnectionFactory.class );
    }

    private Properties getJndiProperties( final CustomJmsProperties customJmsProperties ) {
        Properties jndiProperties = new Properties();
        jndiProperties.setProperty( Context.PROVIDER_URL, customJmsProperties.getProviderUrl() );
        jndiProperties.setProperty( Context.INITIAL_CONTEXT_FACTORY, customJmsProperties.getContextFactoryClass() );
        if ( StringUtils.isNotEmpty( customJmsProperties.getUsername() ) ) {
            jndiProperties.setProperty( Context.SECURITY_PRINCIPAL, customJmsProperties.getUsername() );
        }
        if ( StringUtils.isNotEmpty( customJmsProperties.getPassword() ) ) {
            jndiProperties.setProperty( Context.SECURITY_CREDENTIALS, customJmsProperties.getPassword() );
        }
        return jndiProperties;
    }

    private UserCredentialsConnectionFactoryAdapter getEnhancedUserCredentialsConnectionFactory( final CustomJmsProperties customJmsProperties,
                                                                                                 final ConnectionFactory connectionFactory ) {
        UserCredentialsConnectionFactoryAdapter enhancedConnectionFactory = new UserCredentialsConnectionFactoryAdapter();
        enhancedConnectionFactory.setTargetConnectionFactory( connectionFactory );
        enhancedConnectionFactory.setUsername( customJmsProperties.getUsername() );
        enhancedConnectionFactory.setPassword( customJmsProperties.getPassword() );
        enhancedConnectionFactory.afterPropertiesSet();
        return enhancedConnectionFactory;
    }
}
  

Файл свойств вашего проекта:

 custom.jms.provider-url=tcp://hostname:61616
custom.jms.context-factory-class=org.apache.activemq.jndi.ActiveMQInitialContextFactory
custom.jms.jndi-name=ConnectionFactory