How can I configure RabbitMQ authentication mechanism in Spring Boot? - spring-boot

#Configuration
#EnableRabbit
public class RabbitConfiguration {
private static final String queueName = "3055";
private static final String topicExchangeName = queueName + "-exchange";
#Bean
Queue queue() {
return new Queue(queueName, false);
}
#Bean
TopicExchange exchange() {
return new TopicExchange(topicExchangeName);
}
#Bean
Binding binding(Queue queue, TopicExchange exchange) {
return BindingBuilder.bind(queue).to(exchange).with("foo.bar.#");
}
#Bean
RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory,
MessageConverter messageConverter) {
RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
rabbitTemplate.setMessageConverter(messageConverter);
return rabbitTemplate;
}
#Bean
MessageConverter messageConverter() {
return new Jackson2JsonMessageConverter();
}
}
Code above is my Spring Boot Project's RabbitMQ configuration class.
However, I cannot connect the RMQ server since below error pops up every time I try to connect.
Caused by: com.rabbitmq.client.AuthenticationFailureException: ACCESS_REFUSED - Login was refused using authentication mechanism PLAIN. For details see the broker logfile.
The Server provider told me that I need to set the authentication mechanism to AMQPLAIN.
My question is that How can I set authentication mechanism to AMQPLAIN?
No matter how much I google, I couldn't figure out how.

I confirm to #Raja Anbazhagan. Check the RabbitMQ logs first. Supposedly your user credentials were guest/guest.
The easiest way to solve your problem could be to add those lines in your application.yml:
spring:
rabbitmq:
username: <user-name>
password: <user-password>

Related

Not able to configure durable subscriber in JMS with Spring Boot

I'm using Apache ActiveMQ 5.15.13 and Spring Boot 2.3.1.RELEASE. I'm trying to configure durable subscriber, but I'm not able do do. My application on runtime gives me an error as
Cause: setClientID call not supported on proxy for shared Connection. Set the 'clientId' property on the SingleConnectionFactory instead.
Below is the complete ActiveMQ setup with Spring Boot.
JMSConfiguration
public class JMSConfiguration
{
#Bean
public JmsListenerContainerFactory<?> connectionFactory(ConnectionFactory connectionFactory,
DefaultJmsListenerContainerFactoryConfigurer configurer)
{
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
// This provides all boot's default to this factory, including the message converter
configurer.configure(factory, connectionFactory);
// You could still override some of Boot's default if necessary.
factory.setPubSubDomain(true);
/* below config to set durable subscriber */
factory.setClientId("brokerClientId");
factory.setSubscriptionDurable(true);
// factory.setSubscriptionShared(true);
return factory;
}
#Bean
public MessageConverter jacksonJmsMessageConverter()
{
MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
converter.setTargetType(MessageType.TEXT);
converter.setTypeIdPropertyName("_type");
return converter;
}
}
Receiver Class
public class Receiver {
private static final String MESSAGE_TOPIC = "message_topic";
private Logger logger = LoggerFactory.getLogger(Receiver.class);
private static AtomicInteger id = new AtomicInteger();
#Autowired
ConfirmationReceiver confirmationReceiver;
#JmsListener(destination = MESSAGE_TOPIC,
id = "comercial",
subscription = MESSAGE_TOPIC,
containerFactory = "connectionFactory")
public void receiveMessage(Product product, Message message)
{
logger.info(" >> Original received message: " + message);
logger.info(" >> Received product: " + product);
System.out.println("Received " + product);
confirmationReceiver.sendConfirmation(new Confirmation(id.incrementAndGet(), "User " +
product.getName() + " received."));
}
}
application.properties
spring.jms.pub-sub-domain=true
spring.jms.listener.concurrency=1
spring.jms.listener.max-concurrency=2
spring.jms.listener.acknowledge-mode=auto
spring.jms.listener.auto-startup=true
spring.jms.template.delivery-mode:persistent
spring.jms.template.priority: 100
spring.jms.template.qos-enabled: true
spring.jms.template.receive-timeout: 1000
spring.jms.template.time-to-live: 36000
When i try to run application it gives me error as below
Could not refresh JMS Connection for destination 'message_topic' - retrying using FixedBackOff{interval=5000, currentAttempts=1, maxAttempts=unlimited}. Cause: setClientID call not supported on proxy for shared Connection. Set the 'clientId' property on the SingleConnectionFactory instead.
My application has standalone producer and consumer. I did try to Google the error but nothing helped.
Late answer. Here is what worked for me.
Use SingleConnectionFactory in place of ConnectionFactory
Set client-id to SingleConnectionFactory
Do not set client-id to factory
public JmsListenerContainerFactory<?> connectionFactory(SingleConnectionFactory connectionFactory,
DefaultJmsListenerContainerFactoryConfigurer configurer) {
// Add this
connectionFactory.setClientId("your-client-id")
// Do not do this
//factory.setClientId("brokerClientId");

Access denied to resource: type=<jms>, application=JMSModule, destinationType=queue, resource=test, action=receive

I'm try to receive a weblogic server jms in spring boot . But I have encountered this problem after launch application successfully.
[]org.springframework.jms.listener.DefaultMessageListenerContainer:handleListenerSetupFailure(892): Setup of JMS message listener invoker failed for destination 'jms/test' - trying to recover. Cause: Access denied to resource: type=<jms>, application=JMSModule, destinationType=queue, resource=ns-alert-test, action=receive
And I found it can be connect successfully in thread of [main] when launching.
It looks like the username and password is missed when thread changed
[main] org.springframework.jndi.JndiObjectFactoryBean:lookup(112): Located object with JNDI name [jms/rtsConnectionFactory]
And I'm try to use wlfullclient.jar and wlclient.jar and wlthint3client.jar in my project . But the problem still exists. Can you give me some suggest of this problem . Below is my code
Config:
#Autowired
private JmsErrorHandler jmsErrorHandler;
#Autowired
private JMSPropertiesConfig jmsPropertiesConfig;
#Bean
public JndiTemplate jndiTemplate(){
JndiTemplate jndiTemplate =new JndiTemplate();
Properties properties = new Properties();
properties.setProperty("java.naming.factory.initial","weblogic.jndi.WLInitialContextFactory");
properties.setProperty("java.naming.provider.url", jmsPropertiesConfig.getUrl());
if(jmsPropertiesConfig.getUname()!=null){
properties.setProperty("username", jmsPropertiesConfig.getUname());
}
if(jmsPropertiesConfig.getUcert()!=null){
properties.setProperty("password", jmsPropertiesConfig.getUcert());
}
jndiTemplate.setEnvironment(properties);
return jndiTemplate;
}
#Bean
public JndiDestinationResolver jmsDestionationProvider() {
JndiDestinationResolver destinationResolver = new JndiDestinationResolver();
destinationResolver.setJndiTemplate(jndiTemplate());
return destinationResolver;
}
#Bean
public JndiObjectFactoryBean connectionFactory(){
JndiObjectFactoryBean cf = new JndiObjectFactoryBean();
cf.setJndiTemplate(jndiTemplate());
cf.setJndiName(jmsPropertiesConfig.getFactory());
return cf;
}
#Bean
public JmsTemplate jmsTemplate(){
JmsTemplate template = new JmsTemplate();
template.setConnectionFactory((ConnectionFactory) connectionFactory().getObject());
template.setSessionAcknowledgeModeName("AUTO_ACKNOWLEDGE");
template.setSessionTransacted(true);
template.setDestinationResolver(jmsDestionationProvider());
return template;
}
#Bean
public DefaultJmsListenerContainerFactory jmsListenerContainerFactory() {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
factory.setConnectionFactory((ConnectionFactory) connectionFactory().getObject());
factory.setDestinationResolver(jmsDestionationProvider());
factory.setErrorHandler(jmsErrorHandler);
factory.setSessionAcknowledgeMode(0);
return factory;
}
Receive:
#JmsListener(destination = "jms/test")
public void receiveApplicationNotification(String input) throws Exception {
log.info("Receiving message from jms.external.ExampleQueue.queue "+input);
}
Seems to be a bug in Weblogic.
Can you please try applying below patch..
Bug 22550927 - WEBLOGIC JMS CONNECTION IS NOT THREAD-SAFE

Spring JMS HornetQ user is null

I am trying to connect to a remote HornetQ broker in a spring boot/spring jms application and setup a #JmsListener.
HornetQ ConnectionFactory is being fetched from JNDI registry that HornetQ instance hosts. Everything works fine as long as HornetQ security is turned off but when it is turned on I get this error
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
I ran a debug session to figure out that ConnectionFactory instance being returned is HornetQXAConnectionFactory but user and password fields are not set, which I believe is why user is null. I verified that user principal and credentials are set in JNDI properties but somehow it is not being passed on to ConnectionFactory instance. Any help on how I can get this setup working would be greatly appreciated.
This is my jms related config
#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 and JMS are 100% independent as they are completely different specifications implemented in potentially completely different ways. Therefore the credentials you use for your JNDI lookup do not apply to your JMS resources. You need to explicitly set the username and password credentials on your JMS connection. This is easy using the JMS API directly (e.g. via javax.jms.ConnectionFactory#createConnection(String username, String password)). Since you're using Spring you could use something like this:
#Bean
public ConnectionFactory connectionFactory(JndiObjectFactoryBean connectionFactoryFactory) {
UserCredentialsConnectionFactoryAdapter cf = new UserCredentialsConnectionFactoryAdapter();
cf.setTargetConnectionFactory((ConnectionFactory) connectionFactoryFactory.getObject());
cf.setUsername("yourJmsUsername");
cf.setPassword("yourJmsPassword");
return cf;
}
Also, for what it's worth, the HornetQ code-base was donated to the Apache ActiveMQ project three and a half years ago now and it lives on as the Apache ActiveMQ Artemis broker. There's been 22 releases since then with numerous new features and bug fixes. I strongly recommend you migrate if at all possible.
Wrap the connection factory in a 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.
* ...

AWS SQS (queue) with Spring Boot - performance issues

I have a service that reads all messages from AWS SQS.
#Slf4j
#Configuration
#EnableJms
public class JmsConfig {
private SQSConnectionFactory connectionFactory;
public JmsConfig(
#Value("${amazon.sqs.accessKey}") String awsAccessKey,
#Value("${amazon.sqs.secretKey}") String awsSecretKey,
#Value("${amazon.sqs.region}") String awsRegion,
#Value("${amazon.sqs.endpoint}") String awsEndpoint) {
connectionFactory = new SQSConnectionFactory(
new ProviderConfiguration(),
AmazonSQSClientBuilder.standard()
.withCredentials(new AWSStaticCredentialsProvider(
new BasicAWSCredentials(awsAccessKey, awsSecretKey)))
.withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(awsEndpoint, awsRegion))
.build());
}
#Bean
public DefaultJmsListenerContainerFactory jmsListenerContainerFactory() {
DefaultJmsListenerContainerFactory factory =
new DefaultJmsListenerContainerFactory();
factory.setConnectionFactory(this.connectionFactory);
factory.setDestinationResolver(new DynamicDestinationResolver());
factory.setConcurrency("3-10");
factory.setSessionAcknowledgeMode(Session.CLIENT_ACKNOWLEDGE);
factory.setReceiveTimeout(2000L); //??????????
return factory;
}
#Bean
public JmsTemplate defaultJmsTemplate() {
return new JmsTemplate(this.connectionFactory);
}
I've heard about long polling so I wonder how I could use it in my case. I wonder how this listener works - I do not want to create unnecessary calls to the AWS SQS.
My listener that reads messages and converts them to the Object and saves on Redis db:
#JmsListener(destination = "${amazon.sqs.destination}")
public void receive(String requestJSON) throws JMSException {
log.info("Received");
try {
Trace trace = Trace.fromJSON(requestJSON);
traceRepository.save(trace);
(...)
I'd like to know your opinions - what is the best approach to minimalize unnecessary calls to SQS to get messages.
Maybe shoud I use for example
factory.setReceiveTimeout(2000L);
Unfortunately there is too little information in Internet about it
Thanks,
Matthew

Spring + AMQP serialise object message

I have an application that publishes a message using Spring AMQP’s RabbitTemplate and subscribes to the message on a POJO using MessageListenerAdapter, pretty much as per the Getting Started - Messaging with RabbitMQ guide.
void handleMessage(final String message) {...}
rabbitTemplate.convertAndSend(EXCHANGE_NAME, QUEUE_NAME, "message");
However, this is sending messages as String's; surely there is a way to send and receive Object messages directly?
I have tried registering a JsonMessageConverter but to no avail.
Any help would be greatly appreciated - at present I'm manually de/serialising Strings on either side which seems messy and I'm surprised this isn't a supported feature.
I have tried registering a JsonMessageConverter but to no avail.
It would be better to see your attempt and figure out the issue on our side.
Right now I only can say that you should supply JsonMessageConverter for both sending and receiving parts.
I've just tested with the gs-messaging-rabbitmq:
#Autowired
RabbitTemplate rabbitTemplate;
#Autowired
MessageConverter messageConverter;
.....
#Bean
MessageListenerAdapter listenerAdapter(Receiver receiver, MessageConverter messageConverter) {
MessageListenerAdapter adapter = new MessageListenerAdapter(receiver, "receiveMessage");
adapter.setMessageConverter(messageConverter);
return adapter;
}
#Bean
MessageConverter messageConverter() {
return new Jackson2JsonMessageConverter();
}
.....
System.out.println("Sending message...");
rabbitTemplate.setMessageConverter(messageConverter);
rabbitTemplate.convertAndSend(queueName, new Foo("Hello from RabbitMQ!"));
Where Receiver has been changed to this:
public void receiveMessage(Foo message) {
System.out.println("Received <" + message + ">");
latch.countDown();
}
So, the output is:
Waiting five seconds...
Sending message...
Received <Foo{foo='Hello from RabbitMQ!'}>
when we use Foo like this:
#Override
public String toString() {
return "Foo{" +
"foo='" + foo + '\'' +
'}';
}
with appropriate getter and setter.
Thanks #Artem for the hints. My code is pretty much as per the Getting Started Guide and I had already tried adding a Converter.
But as #Artem has pointed out, the trick is to register the converter with both the container, the listener adapter, and the rabbit template (which was auto-configured in the example).
So my #Configuration class now looks like so, in addition to whatever is mentioned in the Getting Started Guide:
#Bean
SimpleMessageListenerContainer container(final ConnectionFactory connectionFactory, final MessageListenerAdapter messageListenerAdapter,
final MessageConverter messageConverter)
{
final SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
container.setQueueNames(QUEUE_NAME);
container.setMessageListener(messageListenerAdapter);
container.setMessageConverter(messageConverter);
return container;
}
#Bean
RabbitTemplate rabbitTemplate(final ConnectionFactory connectionFactory, final MessageConverter messageConverter)
{
final RabbitTemplate rabbitTemplate = new RabbitTemplate();
rabbitTemplate.setConnectionFactory(connectionFactory);
rabbitTemplate.setMessageConverter(messageConverter);
return rabbitTemplate;
}
#Bean
MessageConverter messageConverter()
{
return new Jackson2JsonMessageConverter();
}
#Bean
Receiver receiver()
{
return new Receiver();
}
#Bean
MessageListenerAdapter listenerAdapter(final Receiver receiver, final MessageConverter messageConverter)
{
return new MessageListenerAdapter(receiver, messageConverter);
}
which means the Receiver can have an Object method signature such as:
void handleMessage(final CbeEvent message)

Resources