How to subscribe to STOMP messages from an application itself - spring

Is there any way how to subscribe from a topic and forward messaged to another layer of an application (have a new Listener for given topic) using Spring?
Consider following message handler it handler which sends messages to a topic topic/chat/{conversationId}
public class ConversationController{
#MessageMapping("/chat/{conversationId}")
#SendTo("/topic/chat/{conversationId}")
public ConversationMessage createMesage(
#Payload CreateMessage message,
#DestinationVariable String conversationId) {
log.info("handleMessage {}", message);
return conversationService.create( message );
}
}
I would like to listen on this topic and do an action on some messages.
public class Bot{
#SubscribeMapping("/topic/chat/{conversationId}")
public void subscribeUserMessages(
#Payload ConversationMessage message,
#DestinationVariable String conversationId){
// doesn't work
}
}
I've also tried use SimpMessagingTemplate.convertAndSend(..) but it doesn't work neither. Maybe I am doing something wrong.
My application doesn't use full flagged message broker, just the default one in memory broker.

Related

RabbitMQ - How multiple consumers can consume same message from single queue?

Most of the RabbitMQ documentation seems to be focused on round-robin, ie where a single message is consumed by a single consumer. I have a requirement wherein would like to receive the same message from a single queue to multiple subscribed consumers.
Below is my sample consumers code. Here there are 2 listeners listening to the same Queue, but the message is getting received by only one of the consumer. How do I configure it so that the same message gets delivered to both the Consumers? (Consumer1 and Consumer2).
Any help will be highly appreciated.
#Component
public class Consumer1 {
#RabbitListener(queues="test.queue.jsa")
public void recievedMessage(Employee msg) {
System.out.println("Recieved Message: " + msg);
}
}
#Component
public class Consumer2 {
#RabbitListener(queues="test.queue.jsa")
public void recievedMessage(Employee msg) {
System.out.println("Consumed Message: " + msg);
}
}
This is not possible; it just doesn't work that way. Each consumer needs its own queue; use a fanout exchange.

AMQP unable to receive message back from listener

I have a issue with Receive message back from Listener to publisher. I am getting
**AmqpReplyTimeoutException **. Below is the code of Publisher from where i am publishing to queue.
for(CsvWrapperPojo item : items){
resultList.addAll(item.getDbResultList());
for(CSVPojo pojo :item.getQueueRequestList()){
sampleResponseMessageRabbitConverterFuture= asyncRabbitTemplate.convertSendAndReceive("spring-boot-rabbitmq-Interactive.async_Solve_InteractiveMsg", "Interactive_RequestQueue", pojo);
//CSVPojo res =(CSVPojo)rabbitTemplate.convertSendAndReceive("spring-boot-rabbitmq-Interactive.async_Solve_InteractiveMsg", "Interactive_RequestQueue", pojo);
System.out.println("heyyyyyy:" + sampleResponseMessageRabbitConverterFuture.get().getLatitute());
//resultList.add(res);
//resultList.add(sampleResponseMessageRabbitConverterFuture.get());
}
}
By using it i am able to publish to queue, i have subscriber code below.
#EnableRabbit
public class ListenerQueueSubscriber {
#RabbitHandler
#RabbitListener(containerFactory = "simpleMessageListenerContainerFactory", queues ="Interactive_RequestQueue")
public void subscribeToRequestQueue(#Payload CSVPojo sampleRequestMessage, Message message) throws InterruptedException {
System.out.println("inside listener");
sampleRequestMessage.setResult("Hello");
Thread.sleep(120000);
System.out.println("After sleep:" +sampleRequestMessage.getLongitude());
//return sampleRequestMessage;
}
}
By using above subscriber able to listen message and i am appending "Hello and put sleep for 2 minutes and after that i have to receive the message back to publisher from where i have published . But unfortunately not receiving the message with Hello appended getting **AmqpReplyTimeoutException **. Can please help to achieve this behavior.
Thanks in advance!!!!

How to subscribe to a specific routing key using RabbitMQ

We are designing a microservices architecture, we would like to use RabbitMQ as message broker.
We wanted each service to have one specific queue, lets say applicationQueue.
We also defined that our messages would be of two kinds:
Events: Messages that are routed to every service. If a service is interested in some specific event, it will intercept it and create a task from it.
Tasks: Messages representing jobs created from the service to himself, they should be publish only to the queue of the service itself
We are struggling to implement that so far using Spring AMQP.
We designed a message producer, so after a given http request, it would create a task for the service itself:
RestController:
#PostMapping
public void saveProduct(#RequestBody Product product) {
messageProducer.message("subscriptions.product.create", product)
.fromHttpRequest(requestContext)
.send();
}
our send method of the message producer:
public void send() {
template.convertAndSend(exchange, routingKey, payload, message -> {
if (requestContext != null) {
extractHttpRequestInfo(message);
message.getMessageProperties().getHeaders()
.put(MessageDictionary.TRANSACTION_ID, generateTransactionId());
} else if (originalMessage != null) {
extractMessageInfo(message);
}
return message;
});
}
RabbitMQ Configuration:
#Bean
List<Binding> binding(Queue queue, TopicExchange exchange) {
return Arrays.asList(
BindingBuilder.bind(queue).to(exchange).with("*.*"),
BindingBuilder.bind(queue).to(exchange).with("${condohub.rabbitmq.queue.name}.#")
);
}
and then subscribe elsewhere (The #Digest annotation is a custom annotation):
#Digest("${condohub.rabbitmq.queue.name}.product.create")
public void createProduct(Product product) {
service.save(product);
}
Any help is welcome.
Your bindings don't make sense; the first one will match all keys with the form foo.bar, baz.qux etc, so the second one is irrelevant.
You should probably just use a fanout exchange for the events and each service has 2 queues, one on the fanout for events and one on the topic exchange for jobs (with a narrow binding for just its own jobs).

how to send and receive from the same topic within spring cloud stream and kafka

I have a spring-cloud-stream application with kafka binding. I would like to send and receive a message from the same topic from within the same executable(jar). I have my channel definitions such as below:-
public interface ChannelDefinition {
#Input("forum")
public SubscriableChannel readMessage();
#Output("forum")
public MessageChannel postMessage();
}
I use #StreamListener to receive messages. I get all sorts of unexpected errors. At times, i receive
No dispatcher found for unknown.message.channel for every other message
If i attach a command line kafka subscriber to the above forum topic, it recieves every other message.
My application receives every other message, which is exclusive set of messages from command line subscriber. I have made sure that my application subscribes under a specific group name.
Is there a working example of the above usecase?
This is a wrong way to define bindable channels (because of the use of the forum name for both). We should be more thorough and fail fast on it, but you're binding both the input and the output to the same channel and creating a competing consumer within your application. That also explains your other issue with alternate messages.
What you should do is:
public interface ChannelDefinition {
#Input
public MessageChannel readMessage();
#Output
public MessageChannel postMessage();
}
And then use application properties to bind your channels to the same queue:
spring.cloud.stream.bindings.readMessage.destination=forum
spring.cloud.stream.bindings.postMessage.destination=forum
Along with the answer above by Marius Bogoevici, here's an example of how to listen to that Input.
#StreamListener
public void handleNewOrder(#Input("input") SubscribableChannel input) {
logger.info("Subscribing...");
input.subscribe((message) -> {
logger.info("Received new message: {}", message);
});
}
For me, consuming from "input" didn't work. I needed to use method name on #Streamlistener and needed to use #EnableBinding, like below:
#Slf4j
#RequiredArgsConstructor
#EnableBinding(value = Channels.class)
public class Consumer {
#StreamListener("readMessage")
public void retrieve(Something req) {
log.info("Received {{}}", req);
}
}

Stomp over websocket using Spring and sockJS message lost

On the client side javascript I have
stomp.subscribe("/topic/path", function (message) {
console.info("message received");
});
And on the server side
public class Controller {
private final MessageSendingOperations<String> messagingTemplate;
ï¼ Autowired
public Controller(MessageSendingOperations<String> messagingTemplate) {
this.messagingTemplate = messagingTemplate;
}
#SubscribeMapping("/topic/path")
public void subscribe() {
LOGGER.info("before send");
messagingTemplate.convertAndSend(/topic/path, "msg");
}
}
From this setup, I am occasionally (around once in 30 page refreshes) experiencing message dropping, which means I can see neither "message received" msg on the client side nor the websocket traffic from Chrome debugging tool.
"before send" is always logged on the server side.
This looks like that the MessageSendingOperations is not ready when I call it in the subscribe() method. (if I put Thread.sleep(50); before calling messagingTemplate.convertAndSend the problem would disappear (or much less likely to be reproduced))
I wonder if anyone experienced the same before and if there is an event that can tell me MessageSendingOperations is ready or not.
The issue you are facing is laying in the nature of clientInboundChannel which is ExecutorSubscribableChannel by default.
It has 3 subscribers:
0 = {SimpleBrokerMessageHandler#5276} "SimpleBroker[DefaultSubscriptionRegistry[cache[0 destination(s)], registry[0 sessions]]]"
1 = {UserDestinationMessageHandler#5277} "UserDestinationMessageHandler[DefaultUserDestinationResolver[prefix=/user/]]"
2 = {SimpAnnotationMethodMessageHandler#5278} "SimpAnnotationMethodMessageHandler[prefixes=[/app/]]"
which are invoked within taskExecutor, hence asynchronously.
The first one here (SimpleBrokerMessageHandler (or StompBrokerRelayMessageHandler) if you use broker-relay) is responsible to register subscription for the topic.
Your messagingTemplate.convertAndSend(/topic/path, "msg") operation may be performed before the subscription registration for that WebSocket session, because they are performed in the separate threads. Hence the Broker handler doesn't know you to send the message to the session.
The #SubscribeMapping can be configured on method with return, where the result of this method will be sent as a reply to that subscription function on the client.
HTH
Here is my solution. It is along the same lines. Added a ExecutorChannelInterceptor and published a custom SubscriptionSubscribedEvent. The key is to publish the event after the message has been handled by AbstractBrokerMessageHandler which means the subscription has been registered with the broker.
#Override
public void configureClientInboundChannel(ChannelRegistration registration) {
registration.interceptors(new ExecutorChannelInterceptorAdapter() {
#Override
public void afterMessageHandled(Message<?> message, MessageChannel channel, MessageHandler handler, Exception ex) {
SimpMessageHeaderAccessor accessor = SimpMessageHeaderAccessor.wrap(message);
if (accessor.getMessageType() == SimpMessageType.SUBSCRIBE && handler instanceof AbstractBrokerMessageHandler) {
/*
* Publish a new session subscribed event AFTER the client
* has been subscribed to the broker. Before spring was
* publishing the event after receiving the message but not
* necessarily after the subscription occurred. There was a
* race condition because the subscription was being done on
* a separate thread.
*/
applicationEventPublisher.publishEvent(new SessionSubscribedEvent(this, message));
}
}
});
}
A little late but I thought I'd add my solution. I was having the same problem with the subscription not being registered before I was sending data through the messaging template. This issue happened rarely and unpredictable because of the race with the DefaultSubscriptionRegistry.
Unfortunately, I could not just use the return method of the #SubscriptionMapping because we were using a custom object mapper that changed dynamically based on the type of user (attribute filtering essentially).
I searched through the Spring code and found SubscriptionMethodReturnValueHandler was responsible for sending the return value of subscription mappings and had a different messagingTemplate than the autowired SimpMessagingTemplate of my async controller!!
So the solution was autowiring MessageChannel clientOutboundChannel into my async controller and using that to create a SimpMessagingTemplate. (You can't directly wire it in because you'll just get the template going to the broker).
In subscription methods, I then used the direct template while in other methods I used the template that went to the broker.

Resources