Я пытаюсь подключиться к удаленному брокеру HornetQ в приложении spring boot / spring jms и настроить @JmsListener.
HornetQ ConnectionFactory загружается из реестра JNDI, на котором размещен экземпляр HornetQ. Все работает нормально, пока защита 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, но поля пользователя и пароля не установлены, что, как я считаю, является причиной того, что user имеет значение null. Я проверил, что основной пользователь и учетные данные установлены в свойствах 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();
}
}




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;
}
Кроме того, как бы то ни было, кодовая база HornetQ была подарена проекту Apache ActiveMQ сейчас три с половиной года назад и продолжает жить как брокер Apache ActiveMQ Artemis. С тех пор было выпущено 22 релиза с множеством новых функций и исправлений ошибок. Я настоятельно рекомендую вам выполнить миграцию, если это вообще возможно.
@Justin Bertram Я использую spring jms, который сам по себе создает соединение и, кажется, использует javax.jms.ConnectionFactory#createConnection() без аргументов.
Я обновил свой ответ потенциальным решением Spring.
Оберните фабрику соединений в 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, поскольку он имеет более подробный ответ
Оберните фабрику соединений в
UserCredentialsConnectionFactoryAdapter.