Spingboot Websocket Stomp - java

Could anyone tell me if the server-side implementation is using stomp WebSocket, is the client also expected to implement stomp?
I am trying to implement a spring boot application and I am confused if I should go with or without stomp implementation. From my research, I understand, if you want to scale the application, it is better to use stomp and embedded broker( RabbitMQ for eg.) as it will handle the sessions, heartbeat etc. instead of an in-memory broker.
The examples available online just shows implementations with and without stomp.
I am basically trying to get different datasets from the table upon client request and write to a WebSocket continuously.
Could anyone please confirm if my understanding so far is correct?
What are the essential things I will have to take care of if I go with stomp + websocket?
Updating the usecase below:
The mobile client would be displaying charts upon user login. There would be links in the left panel for eg. Sales, Discounts etc. which upon clicking, the request will reach server through websocket channel. Server will check the datatype in the request, generate the model using data from DB and write the data to the websocket.
Updating code - v1
MyWebSocketHandler:
#Component
public class MyWebSocketHandler extends TextWebSocketHandler {
Logger logger = LoggerFactory.getLogger(getClass());
#Autowired
DashboardUtil dashboardutil;
#Resource(name = "socketSessionsMap")
private Map<String, WebSocketSession> socketSessionsMap;
#Override
public void handleTextMessage(WebSocketSession session, TextMessage message)
throws InterruptedException, IOException {
try {
//Gets the socket session from map and writes a json to that socket - did for testing purpose.
socketSessionsMap.put("session", session);
//String payload = message.getPayload();
String jsonString = dashboardutil.getDataInJSON(); // gets hardcoded json model
session.sendMessage(new TextMessage(jsonString));
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
}
#Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
}
}
WebSecurityConfig:
#Configuration
#EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
#Autowired
private MyWebSocketHandler myWebSocketHandler;
#Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(myWebSocketHandler, "/socketHandler").setAllowedOrigins("*").withSockJS();
}
}

Could anyone tell me if the server-side implementation is using stomp
WebSocket, is the client also expected to implement stomp?
You can register multiple handlers in your web socket configuration. So in theory you can provide a handler for STOMP and another one for plain web socket. If you only provide a STOMP handler then the handshake from a standard web socket client will fail.
From my research, I understand, if you want to scale the application,
it is better to use stomp and embedded broker( RabbitMQ for eg.) as it
will handle the sessions, heartbeat etc. instead of an in-memory
broker.
That's correct. STOMP also offers a few more nice features especially the subscription to certain endpoints.
I am basically trying to get different datasets from the table upon
client request and write to a WebSocket continuously.
That's a really simple description ...
You should think about if you need to share sessions across multiple instances or if you need to send messages between web socket sessions.
From your description it sounds like you just accept a web socket connection and continuously push data to the client. If you want to scale this application you can just put a load balancer in front of your instances and you are good to go.

Related

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.

Websocket keep track of connections in Spring

Today I've searched a couple of hours for an implementation or tutorial in how to keep track of websocket connections in Spring.
I've done the (very good) Spring tutorial about websockets and STOMP.
link here
So what's my setup, I have an Ionic Hybrid app with an Spring backend and I want to send a notification to the client whenever a new notification-event arises in the backend. All this code is already implemented and the connection works, but right now there is no way to specify where the notifications need to go to.
There is no tutorial or explanation on this matter that follows the structure in the Spring tutorial (at least not after 5 hours of research) and I am a little overwhelmed by all the information about websockets and security on the web. (I've been learning about websockets for just 2 days)
So for all that been before me and will come after me, I think it can be very usefull to have a compact and lightweight answer following the structure taught by the Spring Tutorial.
I've found this unanswered question on StackOverflow about the same problems as I have, so I'm sure this questions will prove it's worth.
TL;DR
How to implement a list in the backend that keeps track of the connections based on the Spring WebSocket Tutorial?
How to send data from the client to the backend when the connection is established? (for example a userid or token)
So I figured it out myself.
My notifications have a recipient id (the user id where the notifications needs to be send to)
So I'm going to send to '/ws-user/'+id+'/greetings' where the id is the user that is logged in.
On the clientside this is fairly easy to achieve.
var stompClient = null;
// init
function init() {
/**
* Note that you need to specify your ip somewhere globally
**/
var socket = new SockJS('http://127.0.0.1:9080/ws-notification');
stompClient = Stomp.over(socket);
stompClient.connect({}, function(frame) {
console.log('Connected: ' + frame);
/**
* This is where I get the id of the logged in user
**/
barService.currentBarAccountStore.getValue().then(function (barAccount) {
subscribeWithId(stompClient,barAccount.user.id);
});
});
}
/**
* subscribe at the url with the userid
**/
function subscribeWithId(stompClient,id){
stompClient.subscribe('/ws-user/'+id+'/greetings', function(){
showNotify();
});
}
/**
* Broadcast over the rootscope to update the angular view
**/
function showNotify(){
$rootScope.$broadcast('new-notification');
}
function disconnect() {
if (stompClient != null) {
stompClient.disconnect();
}
// setConnected(false);
console.log("Disconnected");
}
Next up we add "setUserDestinationPrefix" to the MessageBrokerRegistry in the WebSocketConfig.java class :
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
private final static String userDestinationPrefix = "/ws-user/";
#Override
public void configureMessageBroker(MessageBrokerRegistry config){
config.enableSimpleBroker("/ws-topic","/ws-user");
config.setApplicationDestinationPrefixes("/ws-app");
config.setUserDestinationPrefix(userDestinationPrefix);
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws-notification").setAllowedOrigins("*").withSockJS();
}
}
Note that I'm using internal RestTemplate calls to access my controllermethod that sends out a notification to the subscribed client. This is done by a Event Consumer class (ask to see code, it's just to trigger controller function, could be done differently)
#RequestMapping(value = "/test-notification", method = RequestMethod.POST)
public void testNotification(#RequestBody String recipientId) throws InterruptedException {
this.template.convertAndSendToUser(recipientId,"/greetings", new Notify("ALERT: There is a new notification for you!"));
}
Please review my code and warn me if you see any issues and/or security concerns.
For user based delivery in websocket you can use Principle objects with spring security. Here is a nice example implemented:
https://github.com/rstoyanchev/spring-websocket-portfolio
Spring security will check for SAME ORIGIN and from your client you can send the stomp header with used-id specified.
Hope this might help you.
Take a look at this answer: How to get all active sessions in Spring 5 WebSocket API?
You can retrieve connected users using Spring's SimpUserRegistry API.

Push Message from ActiveMQ to Spring Controller

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.

Session Identification in Tyrus WebSocket API

I have implemented a websocket server which acts as observer for some events.
#ServerEndPoint
public class Server implements SomeObserver
I have implemented objectChanged() from SomeObserver class. The objectChanged() will execute when there is some event will be occur. It is common observer implemnetation.
The application logic is like this:
Clients connect to Websocket server and server sends appropriate events for appropriate clients.
I have coded it like this:
#ServerEndPoint
public class Server implements SomeObserver
{
Session clientSession = null;
#OnOpen
public void OnOpen(Session session}
{
clientSession = session;
}
//implemented OnMessage and OnClose, OnError methods
public void objectChanged(Event[] event)
{
clientSession.sendAsyncRemote().sendObject(someObjectInfo);
}
I never used any session identification. But surprisingly, server sends appropriate messages for respective session. Server does not send one sessions event to another session without any session authentication or identification.
Does anyone know why and how it happens in Tyrus API.
I want to know how Tyrus webocket support session identification.
clientSession.sendAsyncRemote().sendObject(someObjectInfo);
In the above line, the session object will be created per connection basis. It holds the reference socket object (per connection).
Hence, when message is sent, data will be transferred to the respective client.

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