Push message from Java with Spring 4 WebSocket - java

I'd like to push messages from Java to WebSocket clients. I've successfully made a js client send to the server and receive a message back on 2 js clients, so the client side code works fine.
My issue is that I'd like to initiate a send when events occur within the Java app. So for example every time 10 orders have been placed send a message to all subscribed clients. Is this possible?
My current config:
<websocket:message-broker application-destination-prefix="/app">
<websocket:stomp-endpoint path="/hello">
<websocket:sockjs/>
</websocket:stomp-endpoint>
<websocket:simple-broker prefix="/topic"/>
</websocket:message-broker>
#Controller
public class MessageController {
#MessageMapping("/hello")
#SendTo("/topic/greetings")
public Greeting greeting() throws Exception {
return new Greeting("Hello world");
}
}
What I'd like to be able to do is something like this:
public class OrderManager {
#Autowired MessageController messageController;
int orderCount = 0;
public void processOrder(Order o) {
orderCount++;
if(orderCount % 10 == 0)
messageController.greeting();
}
}
and all subscribed clients to the websocket receive a message.

You can use the SimpMessagingTemplate. It's automatically registered. Just autowire it in any Spring bean you want.
#Autowired
private SimpMessagingTemplate template;
...
this.template.convertAndSend("/topic/greetings", text);

Related

How can I send rest request to socket

I tried to create an API that send request via socket.
Code:
#Controller
public class GreetingController {
#GetMapping("/x")
public void send() {
greeting(new HelloMessage("Admin", "bla"));
}
#MessageMapping("/hello")
#SendTo("/topic/greetings")
public Greeting greeting(HelloMessage message) {
return new Greeting(HtmlUtils.htmlEscape(message.getName() + ": " + message.getMsg()));
}
}
I don't understand how to send request via rest to websocket.
Can anyone explain why when I send a request to /x the websocket does not get new HelloMessage?
When you call another method from the same class, you just do that: call a method. This method call doesn't care whether the called method has annotations.
The greeting() method only sends a message when it's called from a websocket client posting a message to /hello.
To send a message programmatically, you use the SimpMessageTemplate, as documented:
What if you want to send messages to connected clients from any part of the application? Any application component can send messages to the brokerChannel. The easiest way to do so is to inject a SimpMessagingTemplate and use it to send messages. Typically, you would inject it by type, as the following example shows:
#Controller
public class GreetingController {
private SimpMessagingTemplate template;
#Autowired
public GreetingController(SimpMessagingTemplate template) {
this.template = template;
}
#RequestMapping(path="/greetings", method=POST)
public void greet(String greeting) {
String text = "[" + getTimestamp() + "]:" + greeting;
this.template.convertAndSend("/topic/greetings", text);
}
}

Spring Integration - Get info from 3rd party services using replyChannel

Im new to Spring Integration, I have to get a list of online agents from 3rd party web services, i tried to configure spring integration to get it, but for the channel part, i not really sure how to configure it.
My original configuration was the following, i copied from a sample that use to send request to 3rd party web services:
public interface WebServiceGateway {
#Gateway(requestChannel = "getStatusChannel")
public String getStatus(String var); <------ being forced to send something
}
In my integration configuration,
#Configuration
public class IntegrationConfiguration {
#Bean
public MessageChannel getStatusChannel() {
return MessageChannels.direct().get();
}
}
The problem is, im not sending any parameter to the webservices, in requestChannel it force me to do so, so i modified the gateway part:
public interface WebServiceGateway {
#Gateway(replyChannel = "getStatusChannel")
public String getStatus();
}
This part remains unchanged:
#Configuration
public class IntegrationConfiguration {
#Bean
public MessageChannel getStatusChannel() {
return MessageChannels.direct().get();
}
}
It prompted me java.lang.IllegalStateException: receive is not supported, because no pollable reply channel has been configured, why can't i use MessageChannel as the reply channel? How should i configure the IntegrationConfiguration?
Please go through this https://spring.io/blog/2014/11/25/spring-integration-java-dsl-line-by-line-tutorial
All you need is to define an IntegrationFlow like below:
IntegrationFlows.from(requestchannel())
.handle("requestHandler","handleInput")
.channel(replyChannel())
.get();

Use #SentTo to send a message with Spring Boot and RabbitMq

Is it possible to send a return value of any method to a queue using an annotation, like
#SentTo("my.queue.name")
String send() {
return myString;
}
Do I definitely need a #RabbitListener to use #SendTo? Maybe another way out?
I'm trying to simplify my code.
#SendTo is only currently for replies from a #RabbitListener where the sender didn't set a replyTo header.
You could do what you want with a Spring Integration #Publisher annotation with its channel wired to a rabbitmq outbound channel adapter...
#Publisher(channel = "amqpOutboundChannel")
public String send() {
return myString;
}
#Bean
#ServiceActivator(inputChannel = "amqpOutboundChannel")
public AmqpOutboundEndpoint amqpOutbound(AmqpTemplate amqpTemplate) {
AmqpOutboundEndpoint outbound = new AmqpOutboundEndpoint(amqpTemplate);
outbound.setRoutingKey("my.queue.name"); // default exchange - route to queue 'my.queue.name'
return outbound;
}
The method has to be public and invoked from outside the bean itself.

Spring Stomp CAN send unsolicited messages

In the Spring WebSocket docs I found this sentence:
It is important to know that a server cannot send unsolicited messages.
http://docs.spring.io/spring/docs/current/spring-framework-reference/html/websocket.html
(25.4.1)
However I tried this code:
#Controller
public class WebsocketTest {
#Autowired
public SimpMessageSendingOperations messagingTemplate;
#PostConstruct
public void init(){
ScheduledExecutorService statusTimerExecutor=Executors.newSingleThreadScheduledExecutor();
statusTimerExecutor.scheduleAtFixedRate(new Runnable() {
#Override
public void run() {
messagingTemplate.convertAndSend("/topic/greetings", new Object());
}
}, 5000,5000, TimeUnit.MILLISECONDS);
}
}
And the message is broadcasted every 5000ms as expected.
So why Spring docs says that a server cannot send unsollicited messages?
The next sentence might mean that in the stomp.js client you are required to set a subscription:
All messages from a server must be in response to a specific client
subscription
But this does not necessarily mean in response to a request. For example a web socket could send information to the following:
Javascript:
stompClient.subscribe('/return/analyze', function(data) {
generateTableData(JSON.parse(data.body));
});
Spring:
#Autowired
private SimpMessagingTemplate simpMessagingTemplate;
public void sendSetpoint(String data) throws Exception {
this.simpMessagingTemplate.convertAndSend("/return/analyze", data);
}
But it cannot send unsolicited messages to the client unless that subscription exists. If this is their intended point it is a little poorly worded.

how to capture subscribe event in my webSocket server with Spring 4

I did a simple web socket communication with spring 4, STOMP and sock.js, following this https://spring.io/guides/gs/messaging-stomp-websocket/
Now I want to upgrade it to simple chat. My problem is that when user subscribes to new chat room, he should get past messages. I don't know how to capture the moment when he subscribed to send him the list of the messages.
I tried using #MessageMapping annotation, but didn't reach any success:
#Controller
public class WebSocketController {
#Autowired
private SimpMessagingTemplate messagingTemplate;
#MessageMapping("/chat/{chatId}")
public void chat(ChatMessage message, #DestinationVariable String chatId) {
messagingTemplate.convertAndSend("/chat/" + chatId, new ChatMessage("message: " + message.getText()));
}
#SubscribeMapping("/chat")
public void chatInit() {
System.out.println("worked");
int chatId = 1; //for example
messagingTemplate.convertAndSend("/chat/" + chatId, new ChatMessage("connected"));
}
}
Then I created that:
#Controller
public class ApplicationEventObserverController implements ApplicationListener<ApplicationEvent> {
#Override
public void onApplicationEvent(ApplicationEvent applicationEvent) {
System.out.println(applicationEvent);
}
}
It works, but captures all possible events, I don't think it is a good practice.
So, my question can be rephrased: how to send initial data when user subscried to sth?
You can return anything directly to a client when it subscribes to a destination using a #SubscribeMapping handler method. The returned object won't go to the broker but will be sent directly to the client:
#SubscribeMapping("/chat")
public Collection<ChatMessage> chatInit() {
...
return messages;
}
On the client side:
socket.subscribe("/app/chat", function(message) {
...
});
Check out the chat example on GitHub, which shows this exact scenario.

Categories