Push Message from ActiveMQ to Spring Controller - java

I'm using Spring MVC, ActiveMQ and WebSocket(via sock.js and stomp.js) to build a real-time data delivery application.
As we know, when a producer(another desktop application) push a message to ActiveMQ, and the onMessage() method will catch it.
public class MessageReceiver implements MessageListener {
public void onMessage(Message message) {
System.out.println(message);
// How to push the message to a Controller in Spring?
}
}
Most of the tutorials just print the message to the console.
I have another controller called WebSocketController:
#Controller
public class WebSocketController {
#SubscribeMapping("/getRealTimeResult.action/{submissionId}")
public Message getRealTimeResultAction(
#DestinationVariable long submissionId) {
return new Message("Message content from onMessage() method");
}
}
I want to push the message received in onMessage() method to the getRealTimeResultAction() method. Can you tell me how to do it?
I know that the ActiveMQ can communicate with the browser using stomp via the port 61613.
I don't want to do this because I think the MQ should be transparent to the user. Also I need to do some authorization in the WebSocketController.

Generally speaking an #Controller with #SubscribeMapping and #MessageMapping methods can handle subscriptions and messages from STOMP clients connected over WebSocket.
From your description it's not clear what you're trying to do. Was the message pushed to ActiveMQ via STOMP from a browser client or was it produced by some other back-end JMS client? Also the MessageReceiver receives an actual message while the #Controller method has an #SubscribeMapping method for handling a subscription from a STOMP client. It's not clear what you're trying to do. Please provide more information so I can provide a better answer.

Related

How can I route messages from a single RabbitMQ queue to separate methods?

I've got a Spring Boot application. I've been designing microservices and these microservices communicate with each other via RabbitMQ. There is a channel service for directing my messages to related microservices. You can see the picture below.
My channel and microservices picture
Channel is directing messages to microservice 1, but microservice 1 should get my message to a specific method, for example, getById() method or createAThing() method.
How can I route my message to the related listener method?
Here is my code that consumes queue:
#RabbitListener(queues = "my-single-queue")
public void createObjectByMessage(Message message) {
// Create object with message
}
#RabbitListener(queues = "my-single-queue")
public void getByIdByMessage(Message message) {
// Get object with message ID
}
Or, how can I consume the message from the same queue but using a different routing key? I have no config class. I'm using #RabbitListener annotation.

STOMP Websocket synchronous communication using Spring

I have a requirement were some of the STOMP websocket connections needs to be handled synchronously.
Meaning I have a client (spring) subscribed to a topic ("/topic").
I have a server (spring) that has defined the broker ("/topic") also defined handlers ("/app/hello").
Now is it possible for the client to send a request to /app/hello and then wait for a response before sending the next request to /app/hello.
How do I return value on my sever (STOMP spec says about RECEIPT frames but I don't think this is something that can be manually controlled).
How do I wait for the value on my client after a send.
To connect a Java client to a websocket endpoint you can use the tyrus reference implementation of JSR356 - Websockets For Java.
Basically you will need to implement a client endpoint (javax.websocket.Endpoint) and a message handler (javax.websocket.MessageHandler). In the endpoint you register the message handler with the current session on open:
public class ClientEndpoint extends Endpoint {
...
#Override
public void onOpen(final Session aSession, final EndpointConfig aConfig) {
aSession.addMessageHandler(yourMessageHandler);
}
}
To connect to the server endpoint you can use the ClientManager:
final ClientManager clientManager = ClientManager.createClient();
clientManager.connectToServer(clientEndpoint, config, uriToServerEndpoint);
The message handler's onMessage method will be invoked, if the server endpoint sends something to the topic.
Depending on your needs you can either choose to implement the mentioned interfaces or use the corresponding annotations.
UPDATE:
The STOMP website lists several implementations of the STOMP protocol. For Java there are Gozirra and Stampy. I have no experience with these frameworks but the examples are pretty straight forward.

How to extend TCP Outbound gateway to just receive messages in Spring Integration

I am developing an web application which supports TCP connection by Spring Integration.
It has two functions.
send messages to Server and receive reply from Server at a time
just receive messages from Server
(The application on server is not developed by Spring Integration.)
In this case, TCP Adapters should be used and I need provide to collaborate TCP Outbound and Inbound Channel Adapters.
But, my application has a few restrictions.
this application has no database.
messages' payload format is already specified. (It means I can not add any correlation data such as a transaction id to payload .
So, I think that collaborating TCP Outbound and Inbound Channel Adapters is difficult. Then, I plan to extend TCP Outbound gateway to add just receiving messages function.
How should I extend it ? (or do you have other ideas?)
Thanks in advance.
If you can determine the message type, something like this should work...
public class ExtendedTcpOutpboundGateway extends TcpOutboundGateway {
private final MessageChannel unsolicitedMessageChannel;
public ExtendedTcpOutpboundGateway(MessageChannel unsolicitedMessageChannel) {
this.unsolicitedMessageChannel = unsolicitedMessageChannel;
}
#Override
public boolean onMessage(Message<?> message) {
if (isUnsolicitedMessage(message)) {
this.messagingTemplate.send(this.unsolicitedMessageChannel, message);
return false;
}
else {
return super.onMessage(message);
}
}
private boolean isUnsolicitedMessage(Message<?> message) {
// TODO Add logic here to determine message type
return false;
}
}
If you can't tell the difference, it's not clear how you could implement your requirements.

How to work with rabbitTemplate receiveAndReply

I have just started experimenting with Spring and rabbitMQ.
I would like to create a microsevice infrastructure with rabbit and spring,
I have been following Spring boot tutorial
But it is very simplistic. As well I am looking at the documentation (springs, Rabbit) for how to create an RPC, i understand the Rabbits approach, but i would like to leverage Spring template to save me the boilerplate.
I just cant seem to understand where to register the reciveAndReplay callback at.
I tried doing this:
sending
System.out.println("Sending message...");
Object convertSendAndReceive = rabbitTemplate.convertSendAndReceive("spring-boot", "send and recive: sent");
System.out.println("GOT " + convertSendAndReceive); //is null
receiving
#Component
public class Receiver {
#Autowired
RabbitTemplate rabbitTemplate;
public void receiveMessage(String message) {
this.rabbitTemplate.receiveAndReply("spring-boot", (Message)->{
return "return this statement";
});
}
}
But its not a big surprise this doesn't work the message is received but nothing comes back. I assume that this needs to be registered somewhere in the factory/template at the bean creation level but i don't seem to understand where and sadly the documentation is unclear.
First, please use the Spring AMQP Documentation.
You would generally use a SimpleMessageListenerContainer wired with a POJO listener for RPC.
The template receiveAndReply method is intended for "scheduled" server-side RPC - i.e. only receive (and reply) when you want to, rather than whenever a message arrives in the queue. It does not block waiting for a message.
If you want to use receiveAndReply(), there's a test case that illustrates it.
EDIT:
This code...
this.template.convertAndSend(ROUTE, "test");
sends a message to the queue.
This code...
this.template.setQueue(ROUTE);
boolean received = this.template.receiveAndReply(new ReceiveAndReplyMessageCallback() {
#Override
public Message handle(Message message) {
message.getMessageProperties().setHeader("foo", "bar");
return message;
}
});
Receives a message and from that queue; adds a header and returns the same messsage to the reply queue. received will be false if there was no message to receive (and reply to).
This code:
Message receive = this.template.receive();
receives the reply.
This test is a bit contrived because the reply is sent to the same queue as the request. We can't use sendAndReceive() on the client side in this test because the thread would block waiting for the reply (and we need to execute the receiveAndReply()).
Another test in that class has a more realistic example where it does the sendAndReceive()s on different threads and the receiveAndReply()s on the main thread.
Note that that test uses a listener container on the client side for replies; that is generally no longer needed since the rabbit broker now supports direct reply-to.
receiveAndReply() was added for symmetry - in most cases, people use a listener container and listener adapter for server-side RPC.

spring websockets: sending a message to an offline user - messages not enqueued in message broker

I have a webapp with spring and websockets using a message broker (activemq).
here is my config class:
#Configuration
#EnableWebSocketMessageBroker
#EnableScheduling
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableStompBrokerRelay("/topic","/queue/");
config.setApplicationDestinationPrefixes("/app");
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/hello").withSockJS();
}
}
I have a scheduled task that constantly pushing messages to the username "StanTheMan" :
#Scheduled(fixedDelay = 1000)
public void sendGreetings() {
HelloMessage hello = new HelloMessage();
hello.setContent("timeStamp:" + System.currentTimeMillis());
String queueMapping = "/queue/greetings";
template.convertAndSendToUser(
"DannyD",
queueMapping,
hello);
}
Currently, if the user is NOT connected via a websocket to the server - all the messages for him are not being en-queued for him, they simply discarded. whenever he connects - fresh messages are being en-queued for him.
Is it possible for me to "convertAndSendToUser" a message to an offline user in any way? i would like to en-queue messages with an expired time for offline users to be later on pushed when they are connecting again and the expired time wasn't over.
how can i achieve that? Obviously using a real message broker (activemq) supposed to help achieving that, but how?
Thanks!
Indeed this feature can only be used to send messages to a (presently) connected user.
We plan to provide better ways to track failed messages (see SPR-10891). In the meantime as a workaround you could inject the UserSessionRegistry into your #Scheduled component and check if the getSessionIds methods returns a non-empty Set. That would indicate the user is connected.
One alternative may be to use your own convention for a queue name that each user can subscribe to (probably based on their user name or something else that's unique enough) in order to receive persistent messages. The ActiveMQ STOMP page has a section on persistent messages and expiration times.
This is a default behavior for message brokers.
And you haven't setup the ActiveMQ broker as your default broker so Spring is setting up a in-memory broker.
To achieve what you want setup/give your details of activeMQ to your spring websocket message broker. As Spring doesn't provide these settings, you have to do all the persistence settings at the ActiveMQ side. .
To setup ActiveMQ for spring websocket message broker you also need to enable stomp and use this:
registry.enableStompBrokerRelay("/topic").setRelayHost("hostName").setRelayPort("00000");
For more information checkout:
http://docs.spring.io/spring/docs/4.0.0.RELEASE/javadoc-api/org/springframework/messaging/simp/config/StompBrokerRelayRegistration.html

Categories