Websocket Broadcasting to different users with Spring web socket with SockJS - java

Currently in my application, a message is broadcast each 10 second with spring websockets. This is how the messages are broadcast to users in my spring application.
#Configuration
#EnableWebSocketMessageBroker
#EnableScheduling
#Component
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
#Autowired
private SimpMessagingTemplate template;
private TaskScheduler scheduler = new ConcurrentTaskScheduler();
public WebSocketConfig() {
System.out.printf(" ---INIT----------");
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/simplemessages").withSockJS();
}
// #Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic/", "/queue/");
config.setApplicationDestinationPrefixes("/app");
}
#PostConstruct
private void broadcastTimePeriodically() {
scheduler.scheduleAtFixedRate(new Runnable() {
public void run() {
try{
template.convertAndSend("/topic/simplemessagesresponse", "{shares:true,price:100.00}");
}catch(MessagingException e){
System.err.println("!!!!!! websocket timer error :>"+e.toString());
}
}
}, 10000);
}
#PreDestroy
private void destroyServices(){
}
// #Override
public void configureClientInboundChannel(ChannelRegistration registration) {
}
// #Override
public void configureClientOutboundChannel(ChannelRegistration registration) {
registration.taskExecutor().corePoolSize(4).maxPoolSize(10);
}
//#Override
public boolean configureMessageConverters(List<MessageConverter> arg0) {
// TODO Auto-generated method stub
return true;
}
#Override
public void configureWebSocketTransport(WebSocketTransportRegistration arg0) {
// TODO Auto-generated method stub
}
}
This is how the browser receives,
var socket = new SockJS(desz);
stompClient = Stomp.over(socket);
stompClient.connect('user', 'guest', function(frame) {
stompClient.subscribe("/topic/simplemessagesresponse", function(servermessage) {
var stompResponse = JSON.parse((servermessage.body));
console.log('server msg: '+stompResponse);
});
});
I want to broadcast same message to some users, while another set of users have another message periodically. How I should modify my above code to achieve this ?

You can have this in your scheduler run() method
this.simpMessagingTemplate.convertAndSend("/queue/" + userGroup.geName(),
messageMap.get(userGroup.geName()));
and in the client side you can subscribe to specific url "queue/{groupName}"
stompClient.subscribe("/queue/${groupName}", function(servermessage) {
var stompResponse = JSON.parse((servermessage.body));
console.log('server msg: '+stompResponse);
});
NOTE :(in client example variable 'groupName' is sent to view from controller and accessed using EL in JSP)

Related

How to call spring boot MessageMapping using okhttp3 Websocket

I have created a spring boot Messaging endpoint and need to create an android chat app and am wondering how I can manage to call those endpoints using okttp Websocket client which does not seem to have a way to add api endpoints like this javascript code.
And here is my spring boot endpoints
#Configuration
#EnableWebSocketMessageBroker
public class WebMessageConfig implements WebSocketMessageBrokerConfigurer {
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker( "/user");
config.setApplicationDestinationPrefixes("/app");
config.setUserDestinationPrefix("/user");
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry
.addEndpoint("/ws")
.withSockJS()
.setAllowedOrigins("*");
}
}
And here is my OkHttp client code
public class StompWs {
private String SERVER_PATH="ws://mydomain.com:8443/MyContex/ws";
public static void main(String[] args) {
try {
new StompWs().run();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private WebSocket webSocket;
public void run() throws Exception {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url(SERVER_PATH).build();
webSocket = client.newWebSocket(request, new SocketListener());
}
private String getData()
{
MessageModel message=new MessageModel();
message.setMessage("Hello");
message.setRecipientId("1");
message.setSenderId("2");
return new Gson().toJson(message);
}
private class SocketListener extends WebSocketListener {
#Override
public void onOpen(WebSocket webSocket, Response response) {
super.onOpen(webSocket, response);
try {
webSocket.send(getData());
/**I need equivalent of this
stompClient.subscribe(
"/user/1/queue/messages",// I need java code to do this
onMessageReceived
*/
}
catch(Exception e)
{
e.printStackTrace();
}
System.out.println("succesfully connected:"+response.toString());//this message execute well
}
#Override
public void onMessage(WebSocket webSocket, String text) {
super.onMessage(webSocket, text);
System.out.println("on message:"+text);
}
#Override
public void onFailure(WebSocket webSocket, Throwable t,
Response response) {
// TODO Auto-generated method stub
super.onFailure(webSocket, t, response);
System.out.println("on message:"+t.toString());
}
}
}

Using OkHttp3 WebSocket with Retrofit to continuously track an android devices location

I have the following java code that I'd like to use in an android app to query an api for continuous lat/lng changes of a device that is running a client app, I want to track the device. I believe the WebSocketCall method I'm attempting to use is deprecated. From what I can tell, there's a problem with how I'm trying to use the webSocket call to create the retrofit client and enqueue the data from the WebSocketListner into retrofit. I've researched several WebSocketListener examples and being a total n00b, I haven't been able to figure out the code. My idea is to keep the connection open to the api via WebSocket and process the data response using retrofit. Any assistance would be greatly appreciated.
private WebSocketCall webSocket;
private void createWebSocket() {
final MainApplication application = (MainApplication) getActivity().getApplication();
application.getServiceAsync(new MainApplication.GetServiceCallback() {
#Override
public void onServiceReady(final OkHttpClient client, final Retrofit retrofit, WebService service) {
User user = application.getUser();
map.moveCamera(CameraUpdateFactory.newLatLngZoom(
new LatLng(user.getLatitude(), user.getLongitude()), user.getZoom()));
service.getDevices().enqueue(new WebServiceCallback<List<Device>>(getContext()) {
#Override
public void onSuccess(retrofit2.Response<List<Device>> response) {
for (Device device : response.body()) {
if (device != null) {
devices.put(device.getId(), device);
}
}
Request request = new Request.Builder().url(retrofit.baseUrl().url().toString() + "api/socket").build();
webSocket = WebSocketCall.create(client, request);
webSocket.enqueue(new WebSocketListener() {
#Override
public void onOpen(WebSocket webSocket, Response response) {
}
#Override
public void onFailure(IOException e, Response response) {
reconnectWebSocket();
}
#Override
public void onMessage(ResponseBody message) throws IOException {
final String data = message.string();
handler.post(new Runnable() {
#Override
public void run() {
try {
handleMessage(data);
} catch (IOException e) {
Log.w(MainFragment.class.getSimpleName(), e);
}
}
});
}
#Override
public void onClose(int code, String reason) {
reconnectWebSocket();
}
});
}
});
}
#Override
public boolean onFailure() {
return false;
}
});
}
So because I'm a total n00b it took some time and a lot of questions to figure this out. Maybe it'll help someone else in the future.
private WebSocket webSocket;
private void createWebSocket() {
final MainApplication application = (MainApplication) getActivity().getApplication();
application.getServiceAsync(new MainApplication.GetServiceCallback() {
#Override
public void onServiceReady(final OkHttpClient client, final Retrofit retrofit, WebService service) {
User user = application.getUser();
map.moveCamera(CameraUpdateFactory.newLatLngZoom(
new LatLng(user.getLatitude(), user.getLongitude()), user.getZoom()));
service.getDevices().enqueue(new WebServiceCallback<List<Device>>(getContext()) {
#Override
public void onSuccess(retrofit2.Response<List<Device>> response) {
for (Device device : response.body()) {
if (device != null) {
devices.put(device.getId(), device);
}
}
Request request = new Request.Builder().url(retrofit.baseUrl().url().toString() + "api/socket").build();
Log.e("WebSockets", "Headers: " + request.headers().toString());
WebSocketListener webSocketListener = new WebSocketListener() {
private static final int NORMAL_CLOSURE_STATUS = 1000;
#Override
public void onOpen(WebSocket webSocket, Response response) {
webSocket.send("{Auth-Token:secret-api-token-here}");
Log.e("WebSockets", "Connection accepted!");
}
#Override
public void onFailure(#NotNull WebSocket webSocket, #NotNull Throwable t, #Nullable Response response) {
reconnectWebSocket();
}
#Override
public void onMessage(#NotNull WebSocket webSocket, #NotNull String text) {
final String data = text;
Log.e("WebSockets", "Receiving : " + text);
handler.post(new Runnable() {
#Override
public void run() {
try {
handleMessage(data);
} catch (IOException e) {
Log.w(MainFragment.class.getSimpleName(), e);
}
}
});
}
#Override
public void onMessage(WebSocket webSocket, ByteString bytes) {
Log.e("WebSockets", "Receiving bytes : " + bytes.hex());
}
#Override
public void onClosing(WebSocket webSocket, int code, String reason) {
webSocket.close(NORMAL_CLOSURE_STATUS, null);
Log.e("WebSockets", "Closing : " + code + " / " + reason);
}
#Override
public void onClosed(#NotNull WebSocket webSocket, int code, #NotNull String reason) {
reconnectWebSocket();
}
};
webSocket = client.newWebSocket(request, webSocketListener);
}
});
}
#Override
public boolean onFailure() {
return false;
}
});
}

Spring WebSocket convertAndSendToUser not working

I am trying to send messages to specific users. I cannot use #SendToUser annotation because in my application the message that will send through the socket will come from another service not ui. Therefore, I am managing the session-user mapping. I tried every possible combination of urls that I found on the internet. I cannot find the problem. Code is shown below.
WebSocketConfig.java
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/user/queue/specific-user");
config.setApplicationDestinationPrefixes("/app");
config.setUserDestinationPrefix("/user");
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/gs-guide-websocket").withSockJS()
.setInterceptors(new HttpHandshakeInterceptor(this.socketStore()));
}
HttpHandshakeInterceptor.java
public class HttpHandshakeInterceptor implements HandshakeInterceptor{
private SocketStore socketStore;
public HttpHandshakeInterceptor(SocketStore socketStore) {
this.socketStore = socketStore;
}
#Override
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,
Map<String, Object> attributes) throws Exception {
if(request instanceof ServletServerHttpRequest)
{
ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request;
String abc = servletRequest.getServletRequest().getHeader("token");
HttpSession session = servletRequest.getServletRequest().getSession();
}
return true;
}
#Override
public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,
Exception exception) {
// TODO Auto-generated method stub
}
}
GreetingController.java
#Controller
public class GreetingController {
#Autowired
private SocketStore socketStore;
#Autowired
private SimpMessageSendingOperations messagingTemplate;
#MessageMapping("/hello")
public void processMessageFromClient(HelloMessage message, SimpMessageHeaderAccessor headerAccessor,
#Header("simpSessionId") String sessionId2) throws Exception {
String sessionId = headerAccessor.getSessionAttributes().get("sessionId").toString();
messagingTemplate.convertAndSendToUser(sessionId, "/queue/specific-user",new Greeting("Hello, " + HtmlUtils.htmlEscape(message.getName()) + "!"), createHeaders(sessionId));
private MessageHeaders createHeaders(String sessionId) {
SimpMessageHeaderAccessor headerAccessor = SimpMessageHeaderAccessor.create(SimpMessageType.MESSAGE);
headerAccessor.setSessionId(sessionId);
headerAccessor.setLeaveMutable(true);
return headerAccessor.getMessageHeaders();
}
}
app.js
var stompClient = null;
function setConnected(connected) {
$("#connect").prop("disabled", connected);
$("#disconnect").prop("disabled", !connected);
if (connected) {
$("#conversation").show();
}
else {
$("#conversation").hide();
}
$("#greetings").html("");
}
function connect() {
var socket = new SockJS('/gs-guide-websocket');
stompClient = Stomp.over(socket);
var random = Math.floor(Math.random() * 11);
stompClient.connect({'token':random}, function (frame) {
setConnected(true);
console.log('Connected: ' + frame);
stompClient.subscribe('/user/queue/specific-user', function (greeting) {
showGreeting(JSON.parse(greeting.body).content);
});
});
}
function disconnect() {
if (stompClient !== null) {
stompClient.disconnect();
}
setConnected(false);
console.log("Disconnected");
}
function sendName() {
stompClient.send("/app/hello", {}, JSON.stringify({'name': $("#name").val()}));
}
function showGreeting(message) {
$("#greetings").append("<tr><td>" + message + "</td></tr>");
}
$(function () {
$("form").on('submit', function (e) {
e.preventDefault();
});
$( "#connect" ).click(function() { connect(); });
$( "#disconnect" ).click(function() { disconnect(); });
$( "#send" ).click(function() { sendName(); });
});

XMPP aSmack - How can I listen on my own user state (Online/Offline) to Reconnect

I am new to xmpp/asmack in android, i'm looking for a method to listen on my own user state and presence changes on the server.
My target it's restore connection if lost.
I'm using presence by roster, which helps me getting the friends presence but actually not the current user itself.
Any help would be appreciated :)
Best regards,
You have to enable a ReconectionManager.
Example:
XmppManager.config = XMPPTCPConnectionConfiguration.builder()
.setServiceName(serverName)
.setHost(server)
.setPort(port)
.build();
connection = new XMPPTCPConnection(config);
ConnectionListener connectionListener = new ConnectionListener(){...}; //
connection.addConnectionListener( connectionListener );
int RECONNECTION_ATTEMPT_SECONDS = 60;
ReconnectionManager.getInstanceFor(connection).enableAutomaticReconnection();
ReconnectionManager.getInstanceFor(connection).setFixedDelay( RECONNECTION_ATTEMPT_SECONDS );
ReconnectionListener looks like this:
public class ReconnectionListener implements ConnectionListener
{
#Override
public void reconnectionSuccessful()
{
System.out.println( "Connection to chat server restored - You are again online" );
//additional foo when connection restored
}
#Override
public void reconnectionFailed( Exception e )
{
System.out.println("Impossible to reconnect, Chat Server seems to be still unavailable" );
}
#Override
public void reconnectingIn( int seconds )
{
System.out.println( "reconnectingIn fired "+seconds);
}
#Override
public void connectionClosedOnError( Exception e )
{
System.out.println("Connection closed, Chat Server become unavailable" );
//additional foo when connection lost (message to user ?)
}
#Override
public void connectionClosed()
{
// "XMPP connection was closed.");
System.out.println( "Connection closed, Chat Server become unavailable");
}
#Override
public void connected( XMPPConnection connection )
{
System.out.println("connected fired - reconnection management enabled");
}
#Override
public void authenticated( XMPPConnection connection, boolean resumed )
{
System.out.println("authenticated fired");
}
}
If that helped, please don't forget to accept the answer :)
To realize the automatic reconnection, you should utilize the ReconnectionManager, and implement the interface ConnectionListener to get the notification.
Details can be refered in https://ramzandroidarchive.wordpress.com/2016/03/14/handling-connection-break-issue-in-smack-4-1/
There exist another way to reconnect by using a timer:
public class TaxiConnectionListener implements ConnectionListener {
private Timer tExit;
private String username;
private String password;
private int logintime = 2000;
#Override
public void connectionClosed() {
Log.i("TaxiConnectionListener", "disconnect");
XmppConnection.getInstance().closeConnection();
tExit = new Timer();
tExit.schedule(new timetask(), logintime);
}
#Override
public void connectionClosedOnError(Exception e) {
Log.i("TaxiConnectionListener", "failed to disconnect");
boolean error = e.getMessage().equals("stream:error (conflict)");
if (!error) {
XmppConnection.getInstance().closeConnection();
tExit = new Timer();
tExit.schedule(new timetask(), logintime);
}
}
class timetask extends TimerTask {
#Override
public void run() {
username = Utils.getInstance().getSharedPreferences("taxicall",
"account", MainActivity.context);
password = Utils.getInstance().getSharedPreferences("taxicall",
"password", MainActivity.context);
if (username != null && password != null) {
Log.i("TaxiConnectionListener", "logining");
if (XmppConnection.getInstance().login(username, password)) {
Log.i("TaxiConnectionListener", "logined");
} else {
Log.i("TaxiConnectionListener", "reconnect");
tExit.schedule(new timetask(), logintime);
}
}
}
}
#Override
public void reconnectingIn(int arg0) {
}
#Override
public void reconnectionFailed(Exception arg0) {
}
#Override
public void reconnectionSuccessful() {
}
}
You need add a connection listener in your logining method:
TaxiConnectionListener connectionListener = new TaxiConnectionListener();
getConnection().addConnectionListener(connectionListener);
Remove listener in disconnection method:
connection.removeConnectionListener(connectionListener);

How to send message to user when he connects to spring websocket

I want to send message to user when he connects to spring websocket, I've
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
#Autowired
private GenervicSerice<User> userService;
#Autowired
private SimpMessagingTemplate template;
private CurrentUser currnetUser;
#Override
public void registerStompEndpoints(StompEndpointRegistry stompEndpointRegistry) {
// TODO Auto-generated method stub
stompEndpointRegistry.addEndpoint("/ws").withSockJS();
}
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/queue/", "/topic/", "/exchange/");
config.setApplicationDestinationPrefixes("/app");
}
#Override
public void configureClientInboundChannel(ChannelRegistration registration) {
registration.setInterceptors(myChannelInterception());
try {
updateNotificationAndBroadcast();
} catch (Exception e) {
return;
}
}
#Bean
public MyChannelInterception myChannelInterception() {
return new MyChannelInterception();
}
private void updateNotificationAndBroadcast() {
try {
template.convertAndSend("/queue/notify", "Greetings");
} catch (Exception e) {
System.out.println("Error message is " + e.getMessage() + "\n\n\n" + "Caused by " + e.getCause()
);
}
}
}
MyChannelInterception class is
public class ImtehanChannelInterception extends ChannelInterceptorAdapter {
private CurrentUser currnetUser;
#Override
public Message<?> preSend(Message<?> message, MessageChannel channel) {
MessageHeaders headers = message.getHeaders();
SimpMessageType type = (SimpMessageType) headers.get("simpMessageType");
String simpSessionId = (String) headers.get("simpSessionId");
currnetUser = new CurrentUser();
if (type == SimpMessageType.CONNECT) {
Principal principal = (Principal) headers.get("simpUser");
currnetUser.setCurrentUserEmail(principal.getName());
System.out.println("WsSession " + simpSessionId
+ " is connected for user " + principal.getName());
} else if (type == SimpMessageType.DISCONNECT) {
System.out.println("WsSession " + simpSessionId
+ " is disconnected");
}
return message;
}
}
through this I get information about new connected user but the method updateNotificationAndBroadcast() in WebSocketConfig is not sending messages to new logged-in users.
I would create SessionSubscribeEvent listener and use SimpMessagingTemplate inside.
Btw, configureClientInboundChannel is called only once (not for every user connected). So you have to handle sending message inside interceptor.
Try something like this:
#Service
public class SomeSubscribeListener {
private SimpMessagingTemplate template;
#Autowired
public SomeSubscribeListener(SimpMessagingTemplate template) {
this.template = template;
}
#EventListener
public void handleSubscribeEvent(SessionSubscribeEvent event) {
template.convertAndSendToUser(event.getUser().getName(), "/queue/notify", "GREETINGS");
}
}
I hope this will help
you need a Websocketconfig file:
package mx.config.ws;
#EnableScheduling
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/chat").withSockJS()
}
#Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
...
}
}
And declare a nother #Configuration file:
package mx.config.ws;
#Configuration
public class WebSocketHandlersConfig {
#Bean
public StompConnectEvent webSocketConnectHandler() {
return new StompConnectEvent();
}
#Bean
public StompDisconnectEvent webSocketDisconnectHandler() {
return new StompDisconnectEvent();
}
}
Next create implementation of ApplicationListener interface. Automatically you will intercept the STOMP connections
package mx.config.ws;
public class StompConnectEvent implements ApplicationListener<SessionConnectEvent> {
#Override
public void onApplicationEvent(SessionConnectEvent event) {
StompHeaderAccessor sha = StompHeaderAccessor.wrap(event.getMessage());
System.out.println("StompConnectEvent::onApplicationEvent() sha.getSessionId(): "+sha.getSessionId()+" sha.toNativeHeaderMap():"+sha.toNativeHeaderMap());
//String company = sha.getNativeHeader("company").get(0);
//logger.debug("Connect event [sessionId: " + sha.getSessionId() +"; company: "+ company + " ]");
// HERE YOU CAN MAYBE SEND A MESSAGE
}
}
Check this link for a bout of information:
http://www.sergialmar.com/2014/03/detect-websocket-connects-and-disconnects-in-spring-4/
The Spring documentation indicates that you need to implement Spring's Application Listener interface.
26. WebSocket Support -> 26.4.14 Events and Interception
The following code is an example for the Session Susbscribe event. you can find all the possible events in the link provided, including the connect event.
#Component
public class VSignNewSubscriptionsListener implements ApplicationListener<SessionSubscribeEvent> {
#Override
public void onApplicationEvent(SessionSubscribeEvent event) {
}
}

Categories