spring - Retrive JMS ConnectionFactory from JNDI using Spring Boot auto-configuration
问题描述
I want to use the spring boot autoconfigure for JMS to connect to a remote JNDI and retrieve the ConnectionFactory based on his name populated through the spring.jms.jndi-name property in the application.properties file.
I noticed that the spring boot autoconfigure is relying on the JndiConnectionFactoryAutoConfiguration class to do that and this class in turn will call the JndiTemplate class to do the lookup. The problem is that the value of the environment attribute of the JndiTemplate class is null, so we cannot create the intialContext.
In fact, I noticed that the JndiTemplate class is instantiated with no-argument constructor in starting application and before loading the configuration defined in the JndiConnectionFactoryAutoConfiguration class.
My question: how can I instantiate JndiTemplate by specifying a list of properties (Context.INITIAL_CONTEXT_FACTORY, Context.PROVIDER_URL..)? knowing that JmsTemplate has a constructor that takes an Properties object.
Just for information: my application is a simple jar that doesn’t run on a server at the moment.
解决方案
For those interested in the answer, you must use VM options to pass required JNDI properties.
Here is an example that works with ActiveMQ:
VM options:
-Djava.naming.provider.url=tcp://hostname:61616
-Djava.naming.factory.initial=org.apache.activemq.jndi.ActiveMQInitialContextFactory
And spring properties file (application.properties) must contain the JNDI name of the connection factory:
spring.jms.jndi-name=ConnectionFactory
Much better, you can use configuration to fin your connection factory from JNDI. In my project, we finished by creating our jms starter that we can use in all microservices.
Properties class:
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;
}
Configuration class:
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;
}
}
Properties file of your project:
custom.jms.provider-url=tcp://hostname:61616
custom.jms.context-factory-class=org.apache.activemq.jndi.ActiveMQInitialContextFactory
custom.jms.jndi-name=ConnectionFactory
推荐阅读
- javascript - 从操作中返回文件并使用 AJAX 下载
- docker - TURN 服务器 turn:xx.xx.xxx.xxx:3479 返回错误,code=701?
- python - 带有 for 循环的“SyntaxError:无法分配给文字”错误
- javascript - 展开/折叠闪亮的 selectInput 功能
- html - 位置固定的左侧属性在多个位置属性中左侧的浏览器中不起作用
- intel - openvino Model Emotions-recognition-retail 中的恐惧和厌恶
- docker - Traefik 2 如何在静态配置中引用 tls 证书以在 docker-compose 文件中进行路由
- node.js - 使用 socket.io 将数据从 express 服务器发送到 react-native 客户端
- python - 我应该怎么做才能减少代码的运行时间?
- windows - 在 docker 而不是 windows 上安装应用程序