spring boot rest and angular2 with websocket (stomp over sockjs) - spring

Is it possible to use stomp over sockjs without MVC. So I would like to have spring rest interface in tomcat, and angular2 application run by express.
WebSocketConfig.java
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
// the endpoint for websocket connections
registry.addEndpoint("/portfolio").setAllowedOrigins("*").withSockJS();
}
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.setApplicationDestinationPrefixes("/app");
config.enableSimpleBroker("/topic");
}
}
SocketController.java
#Controller
public class SocketController {
#Autowired
private SimpMessagingTemplate template;
public SocketController() {
int a = 5;
}
#MessageMapping("/greeting")
public String handle(String greeting) {
return "[" + "greeting" + ": " + greeting;
}
}
and typescript code:
.
.
.
constructor() {
var socket = new SockJS('http://localhost:8080/portfolio');
this.stompClient = Stomp.over(socket);
this.stompClient.connect("guest", "guest", function(frame) {
console.log('Connected: ' + frame);
this.stompClient.subscribe('http://localhost:8080/topic/greeting', function(greeting) {
console.log("from from", greeting);
});
}, function (err) {
console.log('err', err);
});
}
.
.
.
send() {
this.stompClient.send("http://localhost:8080/app/greeting", {}, JSON.stringify({ 'name': "kitica" }));
}
.
.
.
but for some reason this is not working.. in console I get output:
Opening Web Socket...
stomp.js:134 Web Socket Opened...
stomp.js:134 >>> CONNECT
login:guest
passcode:guest
accept-version:1.1,1.0
heart-beat:10000,10000
stomp.js:134 <<< CONNECTED
version:1.1
heart-beat:0,0
stomp.js:134 connected to server undefined
activity-socket.ts:17 Connected: CONNECTED
heart-beat:0,0
version:1.1
and when I send I get
>>> SEND
destination:http://localhost:8080/app/greeting
content-length:17
{"name":"kitica"}
but message never comes back to subscriber.
angular2 is on port 8001 and spring rest is on 8080

The part that was confusing is that I am using spring-boot-rest and I am not serving angular2 as static from tomcat container, I have angular2 under webpack so I was constantly trying to subscribe and send to relative URL.
The right way to do is:
import {Component} from '#angular/core';
import {ActivityService} from '../common/services';
import {MaterializeDirective} from 'angular2-materialize';
import {LarsActionButtonComponent} from '../common/components';
var SockJS = require('sockjs-client');
var Stomp = require('stompjs');
#Component({
selector: 'activity',
providers: [ActivityService],
directives: [MaterializeDirective, LarsActionButtonComponent],
templateUrl: 'app/activity/activity.html'
})
export class Activity {
stompClient: any;
activityId: any;
text: any;
messages: Array<String> = new Array<String>();
constructor() {
}
send() {
this.stompClient.send('/app/hello/' + this.activityId, {}, JSON.stringify({ 'name': this.text }));
}
connect() {
var that = this;
var socket = new SockJS('tst-rest.mypageexample/hello?activityId=' + this.activityId);
this.stompClient = Stomp.over(socket);
this.stompClient.connect({}, function (frame) {
console.log('Connected: ' + frame);
that.stompClient.subscribe('/topic/greetings/' + that.activityId, function (greeting) {
that.messages.push(JSON.parse(greeting.body).content);
});
}, function (err) {
console.log('err', err);
});
}
}
and in spring controller:
#Controller
public class SocketController {
#MessageMapping("/hello")
#SendTo("/topic/greetings")
public Greeting greeting(HelloMessage message) throws Exception {
return new Greeting("Hello, " + message.getName() + "!");
}
}
Configuration class:
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/app");
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/hello").setAllowedOrigins("*").withSockJS();
}
}

i had myself some issues to work with an angular2 client using stomp over SockJs versus a Java spring-boot back-end. Here what i did to make it working:
On the angular side:
this.stompClient.subscribe cannot be find, so bind "this" to "that".
constructor() {
var that = this;
var socket = new SockJS('http://localhost:8080/portfolio');
this.stompClient = Stomp.over(socket);
this.stompClient.connect("guest", "guest", function(frame) {
console.log('Connected: ' + frame);
that.stompClient.subscribe('http://localhost:8080/topic/greeting', function(greeting) {
console.log("from from", greeting);
});
}, function (err) {
console.log('err', err);
});
}
On the Java server side:
Your Controller need an annotation that said where the vallue is returned like this:
#MessageMapping("/greeting")
#SendTo("/topic/greetings")
public String handle(String greeting) {
return "[" + "greeting" + ": " + greeting;
}
According to your message broker.
Hope that's help a bit.

You have send the 'handle()' method value to the subscriber
either using
simpMessagingTemplate.convertAndSend(, );
e.g.- simpMessagingTemplate.convertAndSend("/topic/chats", message.getMessage());
or
#SendTo("destination url") above the handler method.
e.g.- #SendTo("/topic/message")

Related

SpringBoot Websockets gives me a 404 when trying to connect

I'm trying to build a chat using SpringBoot and websockets. When I try to connect to the controller I get a 404 in the debugger. What am I doing wrong?
main.js
function connect(event) {
username = document.querySelector('#name').value.trim();
if (username) {
usernamePage.classList.add('hidden');
chatPage.classList.remove('hidden');
var socket = new SockJS('http://localhost:8080/ws');
stompClient = Stomp.over(socket);
stompClient.connect({}, onConnected, onError);
}
event.preventDefault();
}
WebSocketConfig.java
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws").setAllowedOrigins("*").withSockJS();
}
}

SockJS fails to create/connect to Spring WebSocket

I am learning Spring WebSocket. I have successfully run this Spring WebSocket tutorial. Now I am trying to incorporate it as-is in my existing Spring MVC application. When I run it from Chrome browser, I see below error in its dev console.
Chrome Console
Opening Web Socket...
GET http://localhost:8080/MyAppName/api/gs-guide-websocket/info?t=1497735312528 500 (Internal Server Error) -- abstract-xhr.js:132
Whoops! Lost connection to http://localhost:8080/MyAppName/api/gs-guide-websocket -- stomp.min.js:8
Server Side Error
javax.servlet.ServletException: Could not resolve view with name '/MyAppName/api/gs-guide-websocket/info' in servlet with name 'MyAppName'
at org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1262)
at org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1037)
Client Side
function connect() {
var socket = new SockJS('/MyAppName/api/gs-guide-websocket');
stompClient = Stomp.over(socket);
stompClient.connect({}, function (frame) {
setConnected(true);
console.log('Connected: ' + frame);
stompClient.subscribe('/topic/greetings', function (greeting) {
showGreeting(JSON.parse(greeting.body).content);
});
});
}
Server Side
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/app");
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/MyAppName/api/gs-guide-websocket").withSockJS();
}
}
I have tried to solve this for couple hours now. How do I resolve this?
ty
If your application context is MyAppName then you don't need to specify it in the addEndpoint method - this path is relative to your aaplication context.
Probably registry.addEndpoint("/MyAppName/api/gs-guide-websocket") registers the endpoint with path /MyAppName/MyAppName/api/gs-guide-websocket
Perhaps, mine example will hint the problem
My application base URL
http://localhost:8080/socket/
My js file
function connect() {
var socket = new SockJS('/socket/greeting');
stompClient = Stomp.over(socket);
stompClient.connect({name: 'test'}, function(frame) {
console.log("session Id:" + socket._transport.url);
console.log("user Id:" + socket.current_user_id);
console.log("socket Id:" + stompClient.id);
var sessionId = /\/([^\/]+)\/websocket/.exec(socket._transport.url)[1];
$("#fname").val(sessionId);
console.log("socket Id:" + sessionId);
stompClient.subscribe("/user/queue/errors", function(message) {
alert("Error " + message.body);
});
stompClient.subscribe("/user/queue/reply", function(message) {
showGreeting(message.body);
});
}, function(error) {
alert("STOMP error " + error);
});
}
My socket configuration
package com.connectips.socket.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
import com.connectips.socket.interceptor.HttpHandshakeInterceptor;
#Configuration
#EnableWebSocketMessageBroker
public class SocketConfig implements WebSocketMessageBrokerConfigurer{
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic/", "/queue/");
config.setApplicationDestinationPrefixes("/app");
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/greeting").addInterceptors(new HttpHandshakeInterceptor()).withSockJS();
}
}

Getting error "Failed to parse TextMessage payload=[CONNECT" in STOMP while connecting t

Hi all I trying to connect to STOMP end point in Spring boot app and getting this error when ever I connect to it using STOMP.js and sock.js
o.s.w.s.m.StompSubProtocolHandler : Failed to parse TextMessage payload=[CONNECT
ac..], byteCount=523, last=true] in session b9ql5g3w. Sending STOMP ERROR to client.
org.springframework.messaging.simp.stomp.StompConversionException: Illegal header: ' setConnected(true);'. A header must be of the form <name>:[<value>].
at org.springframework.messaging.simp.stomp.StompDecoder.readHeaders(StompDecoder.java:224) ~[spring-messaging-4.2.5.RELEASE.jar:4.2.5.RELEASE]
at org.springframework.messaging.simp.stomp.StompDecoder.decodeMessage(StompDecoder.java:138) ~[spring-messaging-4.2.5.RELEASE.jar:4.2.5.RELEASE]
at org.springframework.messaging.simp.stomp.StompDecoder.decode(StompDecoder.java:111) ~[spring-messaging-4.2.5.RELEASE.jar:4.2.5.RELEASE]
at org.springframework.messaging.simp.stomp.BufferingStompDecoder.decode(BufferingStompDecoder.java:133) ~[spring-messaging-4.2.5.RELEASE.jar:4.2.5.RELEASE]
at org.springframework.web.socket.messaging.StompSubProtocolHandler.handleMessageFromClient(StompSubProtocolHandler.java:234) ~[spring-websocket-4.2.5.RELEASE.jar:4.2.5.RELEASE]
at org.springframework.web.socket.messaging.SubProtocolWebSocketHandler.handleMessage(SubProtocolWebSocketHandler.java:307) [spring-websocket-4.2.5.RELEASE.jar:4.2.5.RELEASE]
at org.springframework.web.socket.handler.WebSocketHandlerDecorator.handleMessage(WebSocketHandlerDecorator.java:75) [spring-websocket-4.2.5.RELEASE.jar:4.2.5.RELEASE]
at org.springframework.web.socket.handler.LoggingWebSocketHandlerDecorator.handleMessage(LoggingWebSocketHandlerDecorator.java:56) [spring-websocket-4.2.5.RELEASE.jar:4.2.5.RELEASE]
at org.springframework.web.socket.handler.ExceptionWebSocketHandlerDecorator.handleMessage(ExceptionWebSocketHandlerDecorator.java:58) [spring-websocket-4.2.5.RELEASE.jar:4.2.5.RELEASE]
at org.springframework.web.socket.sockjs.transport.session.AbstractSockJsSession.delegateMessages(AbstractSockJsSession.java:385) [spring-websocket-4.2.5.RELEASE.jar:4.2.5.RELEASE]
at ..............................................................
and my javascipt code is
function connect() {
var socket = new SockJS('/hello');
stompClient = Stomp.over(socket);
stompClient.connect({}, function(frame) {
setConnected(true);
console.log('Connected: ' + frame);
stompClient.subscribe('/topic/Presence', function(greeting){
console.log(greeting.body);
showGreeting(JSON.parse(greeting.body).userId,JSON.parse(greeting.body).status,JSON.parse(greeting.body).imageUrl);
});
});
}
I am also using spring boot security. and my Websocket Config and endpont are
#MessageMapping("/hello")
#SendTo("/topic/Presence")
public UserPresences greeting(String message) throws Exception {
Thread.sleep(1000); // simulated delay
return new UserPresences();
}
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/app");
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/hello").withSockJS();
}
}
I was able to solve my problem by changing code on the client side
function connect() {
var socket = new SockJS('/hello');
stompClient = Stomp.over(socket);
stompClient.connect('','', function(frame) {
setConnected(true);
console.log('Connected: ' + frame);
stompClient.subscribe('/topic/Presence', function(greeting){
console.log(greeting.body);
showGreeting(JSON.parse(greeting.body).userId,JSON.parse(greeting.body).status,JSON.parse(greeting.body).imageUrl);
});
});
}

websockets spring glassfish 4

I am trying to build a simple didactic websocket application using spring 4.0, jsf and glassfish 4.0.
I have created a maven web project (because this app has another web component(jsf)), and from this app i`m trying to setup some websockets.
#Configuration
#EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
#Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(echoHandler(), "/echo");
}
#Bean
public WebSocketHandler echoHandler() {
return new EchoHandler();
}
}
and
public class EchoHandler extends TextWebSocketHandler {
#Override
public void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
session.sendMessage(message);
}
}
and on the client side a very simple connect:
<script>
/* <![CDATA[ */
var endPointURL = "ws://localhost:8080/liveasterisk/echo";
var chatClient = null;
function connect () {
chatClient = new WebSocket(endPointURL);
chatClient.onmessage = function (event) {
alert(event);
};
}
function disconnect () {
chatClient.close();
}
function sendMessage() {
chatClient.send("xxx");
}
connect();
/* ]]> */
</script>
The problem is that when the connect() method fires i get a 404 response.
I guess that i have to somehow train jsf to respond to handshake request.
All my *.xhtml are mapped to jsf servlet.
So what I`m I missing here ?
I have solved the problem like this:
#ServerEndpoint(value = "/keepalive", configurator = SpringConfigurator.class)
public class KeepAliveEndpoint {
private static Logger logger = Logger.getLogger(KeepAliveEndpoint.class);
#Autowired
private KeepAliveService keepAliveService;
private List<Session> sessions = new ArrayList<Session>();
#OnOpen
public void onOpen(Session session) {
sessions.add(session);
System.out.println("onOpen: " + session.getId()+" list size: " + sessions.size());
}
#OnClose
public void onClose(Session session) {
System.out.println("onClose: " + session.getId());
sessions.remove(session);
}
#OnMessage
public void handleMessage(Session session, String message) {
try{
Long userId = Long.parseLong(message);
keepAliveService.keepAlive(userId);
}catch(NumberFormatException nfe){
try {
session.getBasicRemote().sendText("Cannot perform live update for your status");
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
so now I have a sockets exposed via jsf and I can inject "services" with #Autowired in this endpoint.
And with this js code:
<script type="text/javascript">
var host = "ws://localhost:8080/myApp/keepalive";
var wSocket = new WebSocket(host);
var browserSupport = ("WebSocket" in window) ? true : false;
// called body onLoad()
function initializeReception() {
if (browserSupport) {
wSocket.onopen = function(){
setInterval(function(){wSocket.send('<h:outputText value="#{managedBean.userDTO.id}" />')}, 300000);
};
// called when a message is received
wSocket.onmessage = function(event) {
alert(event.data);
};
// on error handler
wSocket.onError = function(event) {
alert('An error has occured '+event.data+'.');
};
// called when socket closes
wSocket.onclose = function(){
// websocket is closed.
//alert("Connection is closed...");
};
}
else {
// The browser doesn't support WebSocket
alert("WebSocket is NOT supported by your Browser!");
}
}
initializeReception();
</script>
The above configuration is for use with Spring MVC's DispatcherServlet. Do you have one configured in the web application? Depending on the servlet mapping (not shown above) you'll most likely need one more part added to the URL to match the servlet mapping.
The longer explanation is that #EnableWebSocket creates a HandlerMapping that maps "/echo" to the WebSocketHandler. That HandlerMapping needs to reside in the configuration of the DispatcherServlet in order for the HTTP handshake to be processed.

SockJS secure connection with IE8

Afternoon everyone. I'm having an issue with sockjs and Spring4. I don't know which side of the setup is causing the issue. The problem is I can't seem to get IE8 to open a connection to my Spring backend over HTTPS.
I'm trying to implament this example: https://demo.rasc.ch/spring4ws/
The link I'm trying is the chat.
The link to his source is here: https://github.com/ralscha/spring4ws-demos
The only change I made to his source is I'm using jquery-1.9.1 , Spring 4.0.0, and the full stomp.js and not the stomp.min.js
The sock and stomp code in the index page for the chat client is:
$(function() {
var username, lastUsername, stompClient, content = $("#content")[0],
input = $("#editor input")[0];
function notify(text) {
$('<p class="message notice"/>').text(text).appendTo(content);
content.scrollTop = content.scrollHeight;
}
$(input).keyup(function(event) {
if (event.which === 13 && $.trim(input.value)) {
if (!username) {
username = input.value;
$("#editor p").addClass("user").removeClass("guide").text(username);
var path = window.location.pathname.substring(0,
window.location.pathname.lastIndexOf('/')+1);
var sock = new SockJS(path + 'chat');
stompClient = Stomp.over(sock);
stompClient.connect({}, function(frame) {
notify("The connection has been opened");
$(input).removeAttr("disabled").focus();
stompClient.subscribe("/queue/chatmessage", function(msg) {
var data = JSON.parse(msg.body);
if (lastUsername !== data.username) {
lastUsername = data.username;
$('<p class="user"/>').text(data.username).appendTo(content);
}
$('<p class="message"/>').text(data.message).appendTo(content);
content.scrollTop = content.scrollHeight;
});
},
function(error) {
notify("An error occured: " + error);
$(input).attr("disabled", "disabled");
});
} else {
stompClient.send("/queue/chatmessage", {}, JSON.stringify({username: username, message: input.value}));
}
input.value = "";
}
});
$(input).focus();
$(window).resize(function() {
$(content).height($(window).height() - $("#editor").outerHeight(true) - 15).scrollTop(content.scrollHeight);
}).resize();
});
Sorry about the formatting.
In Spring all I did was separate the the webconfig java file into 2 files
WebConfig is standard. Extends WebMvcConfigurerAdapter :
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index.html");
}
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
WebSocket implaments WebSocketMessageBrokerConfigurer:
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/chat").withSockJS().setSessionCookieNeeded(false);
}
#Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableSimpleBroker("/queue/");
}
#Override
public void configureClientInboundChannel(ChannelRegistration registration) {
// use default thread pool with 1 thread
}
#Override
public void configureClientOutboundChannel(ChannelRegistration registration) {
registration.taskExecutor().corePoolSize(2).maxPoolSize(3);
}
The initilizer is basic too.
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[] { WebConfig.class, WebSocketConfig.class };
}
#Override
protected String[] getServletMappings() {
return new String[] { "/chatdemo/*" };
}
I'm also running this through Tomcat 7 using eclipse. So not the embedded tomcat.
The problem I'm having is the readystate inside sock is being set to permanent in IE. I don't fully understand xhr/xdr polling, but I'm assuming that's the problem.
Is there anything else I need to do to get IE to work over https on the sockjs side or the spring side?

Resources