How to properly configure Stomp and SockJS endpoint in Spring MVC? - spring-mvc

This is/may be duplicate of:
Websocket - InvalidStateError: The connection has not been established yet.
I am implementing Notification System. And want to initialize Socket connection when user Logged In, and show him his notifications, and also if some event happens.
My Code snippet as follows.
websocket.js :
var stompClient = null;
function connect( temp ) {
alert(temp);
//var socket = new SockJS("/websock");
//var socket = new SockJS("/websock"+temp);
var socket = new SockJS(context_path+"/websock"+temp);
//context_path == "/SupportCenter"
stompClient = Stomp.over(socket);
stompClient.connect({}, function( frame ){
console.log( "Connected :- "+frame );
stompClient.subscribe("/topic/notifications", function( notifications ) {
alert( notifications );
});
}, function( error ) {
alert( error );
});
alert();
getNotifications();
}
function getNotifications() {
stompClient.send("/app/hello", {}, "Hiiiiii");
}
WebSocketConfig.java :
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
/* (non-Javadoc)
* #see org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer#registerStompEndpoints(org.springframework.web.socket.config.annotation.StompEndpointRegistry)
*/
#Override
public void registerStompEndpoints(StompEndpointRegistry stompEndpointRegistry) {
stompEndpointRegistry.addEndpoint("/websock").withSockJS();
}
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
// TODO Auto-generated method stub
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/app");
}
}
WebSocketController.java :
#Controller
public class WebSocketController {
#MessageMapping(value="/hello")
#SendTo("/topic/notifications")
public Notify hello() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Notify notify = new Notify();
notify.setMessage("Hello World !!!");
return notify;
}
}
Some code Hom.jsp :
<script type="text/javascript" src="<c:url value="/resources/js/sockjs.min.js"/>"></script>
<script type="text/javascript" src="<c:url value="/resources/js/stomp.min.js"/>"></script>
<script type="text/javascript" src="<c:url value="/resources/js/websocket.js"/>"></script>
<script type="text/javascript">
$(document).ready(function() {
//...
connect( '${nsec}');
});
Why Firefox Console giving XML Parsing Error: no root element found Location: while in Network tab status code is 200 OK.
Console Tab
Network Tab

Originaly posted to this question.
This is because stompClient.connect() method is asynchronous. I doesn't pause the execution waiting until connection is established. When you call getNotifications() right after alert() most probably connection is not established yet (it might be established if alert() takes enough time to connect).
You are supposed to call getNotifications() in stompClient.connect() callback (just like you do with stompClient.subscribe()) to be sure that connection is established by the time it gets invoked.
For example:
stompClient.connect({}, function( frame ){
console.log( "Connected :- "+frame );
stompClient.subscribe("/topic/notifications", function( notifications ) {
alert( notifications );
});
getNotifications();
}, function( error ) {
alert( error );
});
Besides of that you may consider using #SubscribeMapping annotation in your java code to get rid of explicit message from JavaScript to get initial message from the server. This way the server sends initial message as soon as subscription is established.
For example:
#MessageMapping(value="/hello")
#SubscribeMapping("/notifications")
#SendTo("/topic/notifications")
public Notify hello() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Notify notify = new Notify();
notify.setMessage("Hello World !!!");
return notify;
}
Then client code would look like following:
stompClient.connect({}, function( frame ){
console.log( "Connected :- "+frame );
stompClient.subscribe("/topic/notifications", function( notifications ) {
alert( notifications );
});
}, function( error ) {
alert( error );
});

Related

SignalR in the loop

I am using SignalR in loop like this:
int itemsCount = 100;
for (int i = 0; i <= itemsCount; i++)
{
Thread.Sleep(100);
SummaryHub.SendMessages("test", i.ToString());
}
and client site is:
$(function () {
var notifications = $.connection.SummaryHub;
// Create a function that the hub can call to broadcast messages.
notifications.client.broadcastMessage = function (name, message) {
console.log(name);
console.log(message);
};
// Start the connection.
$.connection.hub.start().done(function () {
// $.connection.hub.start({ waitForPageLoad: false }).done(function () {
console.log("connection started")
}).fail(function (e) {
alert(e);
});
});
[HubName("SummaryHub")]
public class SummaryHub : Hub
{
[HubMethodName("sendMessages")]
public static void SendMessages(string name, string message)
{
IHubContext context = GlobalHost.ConnectionManager.GetHubContext<SummaryHub>();
context.Clients.All.broadcastMessage(name, message);
}
}
Problem is that client receive messages after loop is done, not during the loop. How can I fix that? Thanks
I find out. It was ajax call with async:false and that was preventing sending messages to the client. Change async to true solved the problem

SignalR : How can I broadcast message to Other page of the application

I am creating a test app, where one can download some files and on download success notification will be propagated to admin ,something like notification in
www.ge.tt,or panel notification in Facebook.
I have two pages
a)Download.aspx
b)LandingPage.aspx
In Download.aspx
function PushNotification() {
alert("I ran Upto Here");
//Declare a proxy to Reference a Hub
var notification = $.connection.notificationHub;
//Start a Connection
$.connection.hub.start().done(function () {
notification.server.send(21);
//$("#hdnFileId").val()
alert("I ran Upto Here 2 ");
});
notification.client.broadcastMessage = function (FileID) {
alert("file was Downloaded" + FileID);
};
}
Here two different tabs/browser are working Fine showing alert message if page Loads.
but i want to use the brodcast message in my LandingPage.aspx
here is the Js
$(function () {
var notification = $.connection.notificationHub;
notification.client.broadcastMessage = function (FileID) {
alert("file was Downloaded" + FileID);
};
});
And my hubclass ..
namespace TestApplication.Entities
{
public class NotificationHub : Hub
{
//public void Hello()
//{
// Clients.All.hello();
//}
public void Send(int FileID)
{
Clients.All.broadcastMessage(FileID);
}
}
}
but the notification is not coming here, whats wrong Here?
You didn't start connection in LandingPage.aspx
Try like this in LandingPage.aspx
var notification = $.connection.notificationHub;
$.connection.hub.start();
notification.client.broadcastMessage = function (FileID) {
alert("file was Downloaded" + FileID);
};

SignalR - Javascript Hub Proxies

When you use SignalR, in your HTML you need to reference the following two scripts:
<script src="~/Scripts/jquery.signalR-1.0.0.js"></script>
<script src="/signalR/hubs"></script>
The second one is for a JavaScript hub proxy which will be auto generated. Everything works fine. However what happens if the JavaScript Hub Proxy generation is disabled somehow, perhaps by setting DisableJavaScriptProxies property (https://github.com/SignalR/SignalR/commit/7e974f4e92551a26f3e3e0a166e1dbf6c064e850). When JavaScript proxy generation is disabled and you try to reference /signalr/hubs in your HTML, it gives the JavaScript error:
Uncaught Error: SignalR: JavaScript Hub proxy generation has been disabled.
When I browse to that path in the browser, the response is:
throw new Error('SignalR: JavaScript Hub proxy generation has been disabled.')
If the JavaScript proxy generation is disabled, how is the $.connection.myHub.client JavaScript code going to work? What extra do I have to do to make it work? The JavaScript error I get is
Uncaught TypeError: cannot read property 'client' of undefined.
You can create the proxies yourself. See here.
This is also done in the samples project within the SignalR source. See the MouseTracking example. JS for it (from here):
/// <reference path="../../Scripts/jquery-1.8.2.js" />
/// <reference path="../../Scripts/jquery.signalR.js" />
$(function () {
var hubConnection = $.hubConnection('/signalr', { qs: 'test=1', logging: false, useDefaultPath: false }),
hub = hubConnection.createHubProxy('mouseTracking');
hub.on('move', updateCursor);
function updateCursor(id, x, y) {
var e = document.getElementById(id);
if (!e) {
e = $('<div id="' + id + '"><i class="icon-screenshot"></i>' + id + '</div>').appendTo(document.body);
e.css('position', 'absolute');
}
else {
e = $(e);
}
e.css({ left: x + 15, top: y + 15 });
}
hubConnection.logging = true;
hubConnection.start({ transport: activeTransport })
.pipe(function () {
return hub.invoke('join');
})
.pipe(function () {
$(document).mousemove(function (e) {
hub.invoke('move', e.pageX, e.pageY);
updateCursor(hub.state.id, e.pageX, e.pageY);
});
});
});
You may have disabled it in your Startup class, like so:
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
var hubConfiguration = new HubConfiguration();
hubConfiguration.EnableDetailedErrors = true;
hubConfiguration.EnableJavaScriptProxies = false;
app.MapSignalR("/signalr", hubConfiguration);
}
}
I had the code above. Removing/commenting out this line: hubConfiguration.EnableJavaScriptProxies = false; should give you proxy generation.
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
var hubConfiguration = new HubConfiguration();
hubConfiguration.EnableDetailedErrors = true;
app.MapSignalR("/signalr", hubConfiguration);
}
}
For everybody who stumbles on this issue. It seems to be by design, and even the SignalR utility generates only server proxy methods.
It will not create client methods, even if you have a strongly typed Hub (Client interface).
So, the only correct answer, should be that you should generate small functions, as documented at Microsoft.
As in the stockticker sample:
$.connection.hub.start()
.then(init)
.then(function () {
return ticker.server.getMarketState();
})
.done(function (state) {
if (state === 'Open') {
ticker.client.marketOpened();
} else {
ticker.client.marketClosed();
}
The stockticker itself is defined like this
public class StockTickerHub : Hub
and the interface
public interface IClientStock
{
void MarketOpened();
void MarketClosed();
void MarketReset();
void UpdateStockPrice(Stock stock);
}
So for each client proxy method, repeat this. This should not break any bodies project targets.
$.extend(ticker.client, {
updateStockPrice: function (stock) {
var displayStock = formatStock(stock),
$row = $(rowTemplate.supplant(displayStock)),
$li = $(liTemplate.supplant(displayStock)),
bg = stock.LastChange < 0
? '255,148,148' // red
: '154,240,117'; // green
$stockTableBody.find('tr[data-symbol=' + stock.Symbol + ']')
.replaceWith($row);
$stockTickerUl.find('li[data-symbol=' + stock.Symbol + ']')
.replaceWith($li);
$row.flash(bg, 1000);
$li.flash(bg, 1000);
},
marketOpened: function () {
$("#open").prop("disabled", true);
$("#close").prop("disabled", false);
$("#reset").prop("disabled", true);
scrollTicker();
},

Signalr Owin simple example javascript client not being called

I have a 5.3.0 version of signalr self hosting that is being upgraded to a newer version of signalr.
Using https://github.com/SignalR/SignalR/wiki/Self-host example i have created a simple example, but i can’t get it to work.
I can get a connection to the hub on the server and call methods on the hub, but i can’t get the hub to call the javascript client.
When looking at it in fiddler I never see a response come back from the hub.
Here is the code
using System;
using Microsoft.AspNet.SignalR;
using Microsoft.Owin.Hosting;
using Owin;
namespace ConsoleApplication3
{
class Program
{
static void Main(string[] args)
{
string url = "http://localhost:8080/";
using (WebApplication.Start<Startup>(url))
{
Console.WriteLine("Server running on {0}", url);
Console.ReadLine();
}
}
}
}
using Microsoft.AspNet.SignalR;
using Owin;
namespace ConsoleApplication3
{
class Startup
{
// This method name is important
public void Configuration(IAppBuilder app)
{
var config = new HubConfiguration
{
EnableCrossDomain = true,
EnableJavaScriptProxies = true
};
app.MapHubs(config);
}
}
}
using System;
using System.Threading.Tasks;
using Microsoft.AspNet.SignalR;
using Newtonsoft.Json;
namespace ConsoleApplication3.Hubs
{
public class Chat : Hub
{
public override Task OnConnected()
{
Notify(Context.ConnectionId);
return new Task(() => { });
}
public void RunTest()
{
Notify(Context.ConnectionId);
}
public void Notify(string connectionId)
{
dynamic testMessage = new
{
Count = 3,
Message = "Some test message",
Timestamp = DateTime.Now
};
String json = JsonConvert.SerializeObject(testMessage);
var context = GlobalHost.ConnectionManager.GetHubContext<Chat>();
context.Clients.Client(connectionId).sendNotification(json);
}
}
}
And here is the client side
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<script src="Scripts/json2.js"></script>
<script src="Scripts/jquery-1.9.1.js"></script>
<script src="Scripts/jquery.signalR-1.0.1.js"></script>
<script src="http://localhost:8080/signalr/hubs"></script>
<script>
$(function () {
// Proxy created on the fly
var notification = $.connection.chat;
$.connection.hub.logging = true;
// Declare functions that can be run on the client by the server
notification.client.sendNotification = onAddNotification;
notification.client.disconnected = function (connectionid) {
console.log(connectionid);
};
// Testing code only
$("#testButton").click(function () {
// Run test function on server
notification.server.runTest();
});
jQuery.support.cors = true;
// Map the onConnect and onDisconnect functions
notification.client.connected = function () {
alert("Notification system connected");
};
notification.client.disconnected = function () { };
$.connection.hub.url = "http://localhost:8080/signalr";
//$.connection.hub.start();
$.connection.hub.start(function () {
alert("Notification system connected");
});
});
// Process a newly received notification from the server
function onAddNotification(message) {
// Convert the passed json message back into an object
var obj = JSON.parse(message);
var parsedDate = new Date(parseInt(obj.Timestamp.substr(6)));
// Update the notification list
$('#notifications').prepend('<li>' + obj.Message + ' at ' + parsedDate + '</li>');
};
</script>
</head>
<body>
Send test
<ul class="unstyled" id="notifications">
</ul>
</body>
Any ideas would be appreciated, since i am fairly stuck.
Few things in your code:
Change this:
public override Task OnConnected()
{
Notify(Context.ConnectionId);
return new Task(() => { });
}
To:
public override Task OnConnected()
{
Notify(Context.ConnectionId);
return base.OnConnected();
}
Also in your hub:
This function is trying too hard:
public void Notify(string connectionId)
{
dynamic testMessage = new
{
Count = 3,
Message = "Some test message",
Timestamp = DateTime.Now
};
String json = JsonConvert.SerializeObject(testMessage);
var context = GlobalHost.ConnectionManager.GetHubContext<Chat>();
context.Clients.Client(connectionId).sendNotification(json);
}
I'm not even sure why you're passing the connection id (maybe it was meant to be static?)
public void Notify()
{
dynamic testMessage = new
{
Count = 3,
Message = "Some test message",
Timestamp = DateTime.Now
};
Clients.Client(Context.ConnectionId).sendNotification(testMessage);
}
You don't need to serialize twice, we already do it for you.
Remove:
jQuery.support.cors = true;
Never set that.
Also:
// Map the onConnect and onDisconnect functions
notification.client.connected = function () {
alert("Notification system connected");
};
notification.client.disconnected = function () { };
These aren't mapping anything client side. You can't map connected and disconnected from the server to the client. The client has its own events.
Other things:
This should be inside of the start callback so that you don't hit it before it's ready:
$.connection.hub.start().done(function() {
// Testing code only
$("#testButton").click(function () {
// Run test function on server
notification.server.runTest();
});
});

signalR - how to call server method from client - code not working

When I try calling SendNewOrderConfirmation on the server side from the client I get
"undefined" is not a function
on line chat.SendNewOrderConfirmation(data);
does anyone know why is this happening?
Thank you
on the server side
public class DriverChat : Hub, IDisconnect
{
public void Start(Driver d)
{
...
}
public void SendNewOrderConfirmation(OrderDriverData data)
{
LogFile.LogResponseTime(data.orderId, data.driverId);
}
}
on the client side
function begin(args) {
try {
//alert('begin');
chat = $.connection.driverChat;
chat.refresh = function () {
ready++;
};
chat.disconnect = function () {
alert('Server has disconnected');
};
$.connection.hub.start(function () {
chat.start(args);
ready++;
// alert('signalR started');
});
} catch (e) {
alert(e.message);
return e.message;
}
}
function confirmNewOrder(data) {
try{
alert('sending cofirmation');
chat.SendNewOrderConfirmation(data);
alert('confirmation sent');
} catch (e) {
alert(e);
}
};
}
Depending on the version of SignalR which you are using, the answer could be different.
To begin with, if you are on a version prior to v1 Alpha, then your code should be;
chat.sendNewOrderConfirmation(data);
Notice the lowercase s.
If you are on version v1 Alpha or greater, then you code should be;
chat.server.sendNewOrderConfirmation(data);
You have to register the hub's javascript generated file. Add this to your page (or your master page):
<script src="<%: ResolveUrl("~/signalr/hubs") %>"></script>

Resources