【问题标题】:Spring JMS HornetQ user is nullSpring JMS HornetQ 用户为空
【发布时间】:2019-05-28 07:02:08
【问题描述】:

我正在尝试在 spring boot/spring jms 应用程序中连接到远程 HornetQ 代理并设置 @JmsListener

HornetQ ConnectionFactory 正在从 HornetQ 实例托管的 JNDI 注册表中获取。只要关闭 HornetQ 安全性,一切都可以正常工作,但是当它打开时,我会收到此错误

WARN  o.s.j.l.DefaultMessageListenerContainer : Setup of JMS message listener invoker failed for destination 'jms/MI/Notification/Queue' - trying to recover. Cause: User: null doesn't have permission='CONSUME' on address jms.queue.MI/Notification/Queue

我运行了一个调试会话来确定返回的 ConnectionFactory 实例是 HornetQXAConnectionFactory 但未设置用户和密码字段,我相信这就是用户为空的原因。我验证了用户主体和凭据是在 JNDI 属性中设置的,但不知何故它没有被传递给 ConnectionFactory 实例。任何有关如何使此设置正常工作的帮助将不胜感激。

这是我的 jms 相关配置

@Configuration
@EnableJms
public class JmsConfig {

    @Bean
    public JmsListenerContainerFactory<?> jmsListenerContainerFactory(ConnectionFactory connectionFactory,
            DefaultJmsListenerContainerFactoryConfigurer configurer) {
        DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();

        configurer.configure(factory, connectionFactory);

        factory.setDestinationResolver(destinationResolver());
        return factory;
    }

    @Bean // Serialize message content to json using TextMessage
    public MessageConverter jacksonJmsMessageConverter() {
        MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
        converter.setTargetType(MessageType.BYTES);
        converter.setTypeIdPropertyName("_type");
        return converter;
    }

    @Value("${jms.jndi.provider.url}")
    private String jndiProviderURL;
    @Value("${jms.jndi.principal}")
    private String jndiPrincipal;
    @Value("${jms.jndi.credentials}")
    private String jndiCredential;

    @Bean
    public JndiTemplate jndiTemplate() {
        Properties env = new Properties();
        env.put("java.naming.factory.initial", "org.jnp.interfaces.NamingContextFactory");
        env.put("java.naming.provider.url", jndiProviderURL);
        env.put("java.naming.security.principal", jndiPrincipal);
        env.put("java.naming.security.credentials", jndiCredential);
        return new JndiTemplate(env);
    }
    @Bean
    public DestinationResolver destinationResolver() {
        JndiDestinationResolver destinationResolver = new JndiDestinationResolver();
        destinationResolver.setJndiTemplate(jndiTemplate());
        return destinationResolver;
    }

    @Value("${jms.connectionfactory.jndiname}")
    private String connectionFactoryJNDIName;

    @Bean
    public JndiObjectFactoryBean connectionFactoryFactory() {
        JndiObjectFactoryBean jndiObjectFactoryBean = new JndiObjectFactoryBean();
        jndiObjectFactoryBean.setJndiTemplate(jndiTemplate());
        jndiObjectFactoryBean.setJndiName(connectionFactoryJNDIName);
        jndiObjectFactoryBean.setResourceRef(true);
        jndiObjectFactoryBean.setProxyInterface(ConnectionFactory.class);
        return jndiObjectFactoryBean;
    }

    @Bean
    public ConnectionFactory connectionFactory(JndiObjectFactoryBean connectionFactoryFactory) {
        return (ConnectionFactory) connectionFactoryFactory.getObject();
    }
}

【问题讨论】:

    标签: spring-boot spring-jms hornetq


    【解决方案1】:

    JNDI 和 JMS 是 100% 独立的,因为它们是完全不同的规范,以可能完全不同的方式实现。因此,您用于 JNDI 查找的凭证不适用于您的 JMS 资源。您需要在 JMS 连接上显式设置用户名和密码凭据。这很容易直接使用 JMS API(例如通过javax.jms.ConnectionFactory#createConnection(String username, String password))。由于您使用的是 Spring,因此您可以使用以下内容:

    @Bean
    public ConnectionFactory connectionFactory(JndiObjectFactoryBean connectionFactoryFactory) {
        UserCredentialsConnectionFactoryAdapter cf = new UserCredentialsConnectionFactoryAdapter();
        cf.setTargetConnectionFactory((ConnectionFactory) connectionFactoryFactory.getObject());
        cf.setUsername("yourJmsUsername");
        cf.setPassword("yourJmsPassword");
        return cf;
    }
    

    另外,对于它的价值,三年半前的the HornetQ code-base was donated to the Apache ActiveMQ project 现在仍然是Apache ActiveMQ Artemis 经纪人。从那时起已经发布了 22 个版本,其中包含许多新功能和错误修复。我强烈建议您尽可能迁移。

    【讨论】:

    • 将连接工厂包装在UserCredentialsConnectionFactoryAdapter中。
    • @Justin Bertram 我正在使用 spring jms 它自己创建连接,它似乎使用 no-arg javax.jms.ConnectionFactory#createConnection()
    • 我用一个潜在的 Spring 解决方案更新了我的答案。
    【解决方案2】:

    将连接工厂包装在 UserCredentialsConnectionFactoryAdapter 中。

    /**
     * An adapter for a target JMS {@link javax.jms.ConnectionFactory}, applying the
     * given user credentials to every standard {@code createConnection()} call,
     * that is, implicitly invoking {@code createConnection(username, password)}
     * on the target. All other methods simply delegate to the corresponding methods
     * of the target ConnectionFactory.
     * ...
    

    【讨论】:

    • 看起来很有希望。会试一试
    • 它有效,谢谢。您首先指出了解决方案,但我接受@justin_bertram 的回答,因为它有更详细的回复
    猜你喜欢
    • 1970-01-01
    • 2012-10-29
    • 2011-02-12
    • 1970-01-01
    • 2012-10-26
    • 2023-03-08
    • 2011-11-30
    • 2012-05-24
    • 2011-12-23
    相关资源
    最近更新 更多