Signalr - connecting to a remote hub from javascript client code - javascript

My scenario is this:
Server application A saves a assigned task to a database.The change in
database is monitored by a change notification and there is a server hub running.
Client side Javascript code,running along with server application B, has to connect to the remote hub. so that it receives a notification whenever App A inserts into the DB.
Here is my server code
I had downloaded owin.cors package.
[assembly: OwinStartup(typeof(Global))]
namespace Demo
{
public class Global : System.Web.HttpApplication
{
public static void Configuration(IAppBuilder app)
{
app.Map("/signalr", map =>
{
map.UseCors(CorsOptions.AllowAll);
var hubConfiguration = new HubConfiguration
{
EnableDetailedErrors=true,
};
map.RunSignalR(hubConfiguration);
});
}
notificationHub.cs
public class NotificationHub : Hub
{
public static Hashtable UserIdLookupTable = new Hashtable(20);
public static Dictionary<string,Job> PendingNotificationTable = new
Dictionary<string,Job>(20);
public void OnChange(string userId,string task,string description,string
duration)
{
if (UserIdLookupTable.ContainsKey(userId))
{
this.Clients.Client(UserIdLookupTable[userId].ToString()).Notify(userId,
task);
UserIdLookupTable.Remove(userId);
if (PendingNotificationTable.ContainsKey(userId))
PendingNotificationTable.Remove(userId);
}
else
PendingNotificationTable.Add(userId, new Job(userId, task,
description, duration));
}
public override Task OnConnected()
{
string name =Context.QueryString["userId"];
registerConnectionId(name);
return base.OnConnected();
}
public void registerConnectionId(string userId)
{
if (UserIdLookupTable.ContainsKey(userId))
UserIdLookupTable[userId] = Context.ConnectionId;
else
UserIdLookupTable.Add(userId, Context.ConnectionId);
if(PendingNotificationTable.ContainsKey(userId))
{
Job j=PendingNotificationTable[userId];
OnChange(j.UserId, j.Description, j.EmployeeName, j.Duration);
}
}
Client Side Code connecting to the remote hub
My script includes
<script src="~/Scripts/jquery-1.6.4.min.js"></script>
<script src="~/Scripts/jquery.signalR-2.2.2.min.js"></script>
<script src="~/Scripts/HubConnection.js"></script>
HubConnection.js
function ConnectToHub()
{
jQuery.support.cors = true;
$.connection.hub.url = "http://myip:56698";
$.connection.hub.qs = { 'UserId' : '35' };
var connection = $.hubConnection();
var hub = connection.createHubProxy('NotificationHub');
hub.on('Notify', function(userName, message) {
console.log(userName + ' ' + message);
});
connection.logging = true;
connection.start().done(function () {
console.log('Now connected, connection ID=' + connection.id);
})
.fail(function (a)
{
console.log('Could not connect'+ a );
});
}
While debugging using chrome, it comes to connection.start and does not go in to success or fail.Just leaves the script.There is no sign that it has connected to the server.
It has to hit OnConnected on server right?
Wondering whether i have missed something.
The above client code is all i did on the client project (apart from installing signalr.client package.) Thats enough right?

Related

Setting relative URL for javascript (websocket connecting to Spring MVC)

I'm following this tutorial : https://www.baeldung.com/websockets-spring
I tested the app and it works perfectly when running on the embedded-tomcat server. However, when I try to deploy and run the same app on an external tomcat server it breaks, because instead of the URL being
localhost:8080/chat
it becomes
myhostIP:port/spring-boot-web-jsp/chat
So I modified the javascript file adding /spring-boot-web-jsp in front of the existing URLs. When I run the webapp the sockets connect successfully and send data. However now my Spring MVC Controller doesn't work.
My javascript :
var stompClient = null;
function setConnected(connected) {
document.getElementById('connect').disabled = connected;
document.getElementById('disconnect').disabled = !connected;
document.getElementById('conversationDiv').style.visibility
= connected ? 'visible' : 'hidden';
document.getElementById('response').innerHTML = '';
}
function connect() {
var socket = new SockJS('/spring-boot-web-jsp-1.0/chat');
stompClient = Stomp.over(socket);
stompClient.connect({}, function(frame) {
setConnected(true);
console.log('Connected: ' + frame);
stompClient.subscribe('/spring-boot-web-jsp-1.0/topic/messages', function(messageOutput) {
showMessageOutput(JSON.parse(messageOutput.body));
});
});
}
function disconnect() {
if(stompClient != null) {
stompClient.disconnect();
}
setConnected(false);
console.log("Disconnected");
}
function sendMessage() {
var from = document.getElementById('from').value;
var text = document.getElementById('text').value;
stompClient.send("/spring-boot-web-jsp-1.0/app/chat", {},
JSON.stringify({'from':from, 'text':text}));
}
function showMessageOutput(messageOutput) {
var response = document.getElementById('response');
var p = document.createElement('p');
p.style.wordWrap = 'break-word';
p.appendChild(document.createTextNode(messageOutput.from + ": "
+ messageOutput.text + " (" + messageOutput.time + ")"));
response.appendChild(p);
}
My Controller :
#MessageMapping("/chat")
#SendTo("/topic/messages")
public OutputMessage send(Message message) throws Exception {
String time = new SimpleDateFormat("HH:mm").format(new Date());
return new OutputMessage(message.getFrom(), message.getText(), time);
}
My message broker :
#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("/chat");
registry.addEndpoint("/chat").withSockJS();
}
}
I've tried modifying the Controller to :
#MessageMapping("app/chat")
#SendTo("/topic/messages")
public OutputMessage send(Message message) throws Exception {
String time = new SimpleDateFormat("HH:mm").format(new Date());
return new OutputMessage(message.getFrom(), message.getText(), time);
}
#MessageMapping("spring-boot-web-jsp-1.0/app/chat")
#SendTo("spring-boot-web-jsp-1.0/topic/messages")
public OutputMessage send(Message message) throws Exception {
String time = new SimpleDateFormat("HH:mm").format(new Date());
return new OutputMessage(message.getFrom(), message.getText(), time);
}
and a bunch of other variations but none of them work.
How can I modify the Controller and javascript file to work when testing through external Apache Tomcat as well as embedded (setting a relative URL of some sort)? And how can I get this to work properly on the external Tomcat?
Remove your tomcat/webapps/ROOT directory
Rename your final jar/war/ear file to ROOT.jar/war/ear
Deploy it on tomcat
Tomcat will deploy your app under root directory localhost:8080/chat

How to send message from node.js server to Android app?

Im trying to make a simple application. That is When I write a word at edittext in android app such as "Hi", Then android app send message "Hi" to node.js server and node.js server send message "Hi has sent successflly" to android app. This is just a example, actually my object is android send a data(message) to server, and receive another data(message) from server.
The problem is this. When I write a word at android app and press button, the message transmitted successfully(I can confirm by console at node.js). But I cant send message to android from node.js .. When I press send button, My android app shut down..
What android says is "java.lang.NullPointerException: Attempt to invoke virtual method 'void android.app.Activity.runOnUiThread(java.lang.Runnable)' on a null object reference" ..
Yesterday, this error didn't happened and another error occured. "cannot cast string to JSONObject."
I will show you my code.
Server Side(Node.js)
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
var port = 12000;
app.get('/', function(req, res) {
res.sendFile('index.html');
})
io.on('connection', function(socket) {
console.log('Android device has been connected');
socket.on('message', function(data) {
console.log('message from Android : ' + data);
Object.keys(io.sockets.sockets);
Object.keys(io.sockets.sockets).forEach(function (id) {
console.log("ID : ", id );
io.to(id).emit('message', data);
console.log(data + ' has sent successfully');
})
/*if (data != null) {
io.emit('message', {message : data + ' has received successfully'});
}*/
})
socket.on('disconnect', function() {
console.log('Android device has been disconnected');
})
})
http.listen(port, function() {
console.log('Server Start at port number ' + port);
})
Client Side (Android)
private Emitter.Listener handleIncomingMessages = new Emitter.Listener(){
#Override
public void call(final Object... args){
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
JSONObject data = (JSONObject) args[0];
String message;
try {
message = data.getString("text").toString();
Log.i("result", message);
addMessage(message);
} catch (JSONException e) {
Log.e("result", "Error : JSONException");
return;
} catch (ClassCastException e) {
Log.e("result", "Error : ClassCastException");
e.printStackTrace();
return;
}
}
});
}
};
private void sendMessage(){
String message = mInputMessageView.getText().toString().trim();
mInputMessageView.setText("");
addMessage(message);
JSONObject sendText = new JSONObject();
try{
sendText.put("text", message);
socket.emit("message", message);
}catch(JSONException e){
}
}
private void addMessage(String message) {
mMessages.add(new Message.Builder(Message.TYPE_MESSAGE)
.message(message).build());
// mAdapter = new MessageAdapter(mMessages);
mAdapter = new MessageAdapter( mMessages);
mAdapter.notifyItemInserted(0);
scrollToBottom();
}
private void scrollToBottom() {
mMessagesView.scrollToPosition(mAdapter.getItemCount() - 1);
}
I already searched similar problems that other people asked, but It didn't give me solution. Please help me. Thank you for reading long question.
p.s Because Im not English speaker, Im not good at English .. There will be many problems at grammar and writing skills. Thanks for understanding...
Reason this happens is because method getActivity() returns null. This might happen if you run this on a fragment after it is detached from an activity or activity is no longer visible. I would do a normal null check before like:
Activity activity = getActivity();
if(activity != null) {
activity.runOnUiThread(new Runnable() {...}
}
I'm not familiar with socket.emit() method but it might throw network exception since it's running on UI thread and you are not allowed to do that. I recommend using RxJava/RxAndroid if you want to do this on another thread.
If you want to do network operation just use it like this:
Observable
.fromRunnable(new Runnable {
void run() {
// here do your work
}
})
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<Void>() {
#Override
public void onCompleted() {
// not really needed here
}
#Override
public void onError(Throwable e) {
// handle errors on UI thread
}
#Override
public void onNext(Void void) {
// do something on UI thread after run is done
}
});
Basically what it does it calls method call from Callable you just made on separate thread and when it's over it invokes onNext method if no exception was thrown or onError method if exception was thrown from Subscriber class.
Note that Response class isn't part of the RxJava/RxAndroid API and you can make it if you want. You can make it a simple POJO class or anything else you need it to be. If you don't need to have response you can use Runnable instead of Callable and it will work just fine.
In order for this to work you need to add this dependencies to your modules Gradle file:
dependencies {
compile 'io.reactivex:rxandroid:1.2.1'
compile 'io.reactivex:rxjava:1.1.6'
}

How to define Spring WebSocket subscriber path

I want to know how to define the subscriber path.
For instance, declaration of subscribing path
stompClient.subscribe("/topic/simplemessagesresponse", function(servermessage) {
Why there are two parts 'topic' and 'simplemessageresponse' .. what they refere. How many such domain parts can be there and why ? My question is on not only for the client side, but also server side . SimpMessagingTemplate.convertAndSend("/topic/simplemessagesresponse", "Message to client");
There are tutorials showing the websocket server and client samples. But no enough details of rules to declare the subscriber path and how the subscriber path could be found.
What are the dependencies to change the path when it is declared in server and client side. I think another similar question is raised because of the a location change of a page where the websocket client is written.
Quoting the STOMP spec documentation:
Note that STOMP treats this destination as an opaque string and no
delivery semantics are assumed by the name of a destination. You
should consult your STOMP server's documentation to find out how to
construct a destination name which gives you the delivery semantics
that your application needs.
That means that destination semantics is broker specific:
For RabbitMQ: check out the Destinations section under the STOMP
plugin documentation- http://www.rabbitmq.com/stomp.html For
For ActiveMQ: check out the Working with Destinations with Stomp -
https://activemq.apache.org/stomp.html
I have implemented the websocket stomp by following this blog.
I replaced #SendTo by SimpMessagingTemplate.
Here is my sample ChatController
#Autowired
private SimpMessagingTemplate simpMessagingTemplate;
#MessageMapping("/dualchart")
#ResponseBody
public void dualchat(MessageDTO message) {
// forward message to destination
String destination = "/topic/dualchat/" + message.getToUser();
simpMessagingTemplate.convertAndSend(destination, message);
}
MessageDTO
#JsonIgnoreProperties
public class MessageDTO extends BaseModel {
private String fromUser;
private String toUser;
private String message;
public String getFromUser() {
return fromUser;
}
public void setFromUser(String fromUser) {
this.fromUser = fromUser;
}
public String getToUser() {
return toUser;
}
public void setToUser(String toUser) {
this.toUser = toUser;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
Web Socket Config
<websocket:message-broker application-destination-prefix="/app">
<websocket:stomp-endpoint path="/dualchat">
<websocket:sockjs />
</websocket:stomp-endpoint>
<websocket:simple-broker prefix="/topic" />
</websocket:message-broker>
Javascript
var socket = new SockJS("/starter.web.admin/dualchat");
var stompClient = Stomp.over(page.socket);
stompClient.connect({}, socketJsConnectedCallback, socketJsErrorCallback);
function socketJsConnectedCallback() {
var myId = "111"; // replace this Id
stompClient.subscribe('/topic/dualchat/' + myId, function(message) {
console.log("you reveived a message::::::::::" + JSON.stringify(message));
// you have message, and you can do anything with it
});
}
function socketJsErrorCallback(error){console.log(error);}
function sendMessage(message) {
var data = {
toUser : "1",
message : message
}
stompClient.send("/app/dualchat", {}, JSON.stringify(data );
}
Hope this will help next search...

Use SignalR to notify from wcf to JS

I want to use SignalR to recognize Jquery client on WCF service and board him messages even without a request. (Ie after the first request the client sends to the service, the service can to know him and send him messages).
I do not know if this is the best way to do this, but it's all I could find. (Except WebSocket that are supported only in VS 2012).
I added in the Global file on the Service the following function:
protected void Application_Start(object sender, EventArgs e)
{
RouteTable.Routes.MapHubs();
}
I created Chat.cs:
public class Chat : Hub
{
public void Send(string message)
{
// Call the addMessage method on all clients
Clients.All.addMessage(message);
}
}
In the JS project I added the JS files of SignalR:
<script src="../JavaScript/jquery.signalR-1.1.2.min.js" type="text/javascript"></script>
<script src="/signalr/hubs" type="text/javascript"></script>
Function that uses it:
function Chat() {
$(function () {
// Proxy created on the fly
var chat = $.connection.chat;
// Declare a function on the chat hub so the server can invoke it
chat.client.addMessage = function (message) {
alert(message);
};
// Start the connection
$.connection.hub.start().done(function () {
// Call the chat method on the server
chat.server.send('aa');
});
});
}
This is something new to me so sorry if the question stupid, but how the SignalR of JS should know the Service, where I should define him? (This calls for a Cross-Domain)
(The variable $.connection.chat is Undefined)
I'm sure I missed a few things, especially the main thing that he how to link the service and JS via SignalR?
What I was missing is the use SignalR with cross-domain.
On Global file I changed the code:
protected void Application_Start(object sender, EventArgs e)
{
RouteTable.Routes.MapHubs(new HubConfiguration() { EnableCrossDomain = true });
}
It is important to note here that since I use cross-domain I have code in Application_BeginRequest function that should cancel it when done SignalR request otherwise it does not work. So I canceled it this way:
protected void Application_BeginRequest(object sender, EventArgs e)
{
//Here is testing whether this request SignalR, if not I do the following code
if (HttpContext.Current.Request.Path.IndexOf("signalr") == -1)
{
HttpContext.Current.Response.Cache.SetCacheability(HttpCacheability.NoCache);
HttpContext.Current.Response.Cache.SetNoStore();
HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", "*");
if (HttpContext.Current.Request.HttpMethod == "OPTIONS")
{
HttpContext.Current.Response.AddHeader("Access-Control-Allow-Methods", "GET, POST");
HttpContext.Current.Response.AddHeader("Access-Control-Allow-Headers", "Content-Type, Authorization, Accept, x-requested-with");
HttpContext.Current.Response.AddHeader("Access-Control-Max-Age", "1728000");
HttpContext.Current.Response.End();
}
}
}
On the client:
I added the scripts of SignalR and Jquery, and I deleted this script:
<script src="/signalr/hubs" type="text/javascript"></script>
Because there's cross-domain calls it is not needed.
The connection is in the following function:
var connection;
var contosoChatHubProxy;
function RegisterToServiceMessege() {
connection = $.hubConnection();
connection.url = 'http://localhost:xxx/signalr';
contosoChatHubProxy = connection.createHubProxy('ChatHub');
//This part happens when function broadcastMessage is activated(from the server)
contosoChatHubProxy.on('broadcastMessage', function (userName, message) {
alert('You have a messege:\n' + userName + ' ' + message);
});
connection.start()
.done(function () {
console.log('Now connected, connection ID=' + connection.id);
})
.fail(function () { console.log('Could not connect'); });
}
Chat.cs on the server:
[HubName("ChatHub")]
public class Chat : Hub
{
[HubMethodName("Send")]
public void Send(string name, string message)
{
// Call the broadcastMessage method to update clients.
Clients.All.broadcastMessage(name, message);
}
}
Call to a function Send from one of customers is as follows:
function SendMessege() {
contosoChatHubProxy.invoke('Send', 'aaa', 'bbb').done(function () {
console.log('Invocation of NewContosoChatMessage succeeded');
}).fail(function (error) {
console.log('Invocation of NewContosoChatMessage failed. Error: ' + error);
});
}
All clients receive the sent message.
(You can check this by running several browsers simultaneously.)
Since version 2.0 of SignalR you can no longer enable Cors using this code:
protected void Application_Start(object sender, EventArgs e)
{
RouteTable.Routes.MapHubs(new HubConfiguration() { EnableCrossDomain = true });
}
Instead, you must add those two lines in your Startup class initializing method:
public void Configuration(IAppBuilder app)
{
app.UseCors(CorsOptions.AllowAll);
app.MapSignalR();
}
And, what may not be obvious for everyone, if you don't want to host SignalR using its self-hosting library, then remember to change your project to WebApplication. In my case I was trying to append Signalr to WCFProject. And runtime wouldn't even go to this Configuration method of Startup, thus causing constant errors forbidding access to signalr resources

Flash as a socket gateway for javascript

i just completed a web based chat application based on ajax/php. But the problem with this app is that it has to continuously poll server to check for new messages, which in turn overloads the server if many people are using this app simultaneously.
now i want to implement a socket based chat app in JavaScript. I know there is no support for sockets in JavaScript so i decided to use "Flash as a socket gateway for JavaScript" i am using Linux and and new to flash. can someone help me with how to achieve this.
basically,
1) I want to make a small SWF object that just handles socket logic(minimum width and height so i can hide it easily with -ve margin.
2) I want to access this swf object with JavaScript
i got a code for simple socket in actionscript (from internet) but i cannot get it to compile using mxmlc(free flash compiler).
heres the code...
myXML = new XMLSocket;
myXML.onConnect = handleConnect;
myXML.onXML = handleXML;
myXML.onClose = handleDisconnect;
myXML.connect("http://www.yourServer.com", 12345);
function handleConnect(connectionStatus){
connectionStatus ? trace("Connected.") : trace("Connection failed.");
}
function handleXML(xmlObject){
trace("Object recieved:: "+xmlObject);
}
function sendXML(textToSend){
myXML.send(new XML('"+textToSend+""));
}
function handleDisconnect(){
trace("Connection lost.");
}
function closeConnection(){
trace("Closing connection to server.");
myXML.close();
}
i got a better code but this also does not compile
package
{
import flash.errors.*;
import flash.events.*;
import flash.net.Socket;
public class ChatSocket extends Socket
{
public var host:String;
public var port:uint;
private var socket:Socket;
public static var SOCK_CONNECTED:String = "onSockConnect";
public static var SOCK_IOERROR:String = "onSockIOError";
function ChatSocket(h:String, p:uint)
{
host = h;
port = p;
socket = this;
super(host, port);
initListeners();
}
public function sendMessage(str:String):void
{
if(connected)
{
socket.writeUTFBytes(str + "\n");
}
else
{
trace("Not connected, message not sent!");
}
}
public function readMessage():void
{
if(connected)
{
var str:String = socket.readUTFBytes(socket.bytesAvailable);
trace("Socket Server Response: " + str);
}
else
{
trace("No message read, not connected!");
}
}
private function initListeners():void
{
socket.addEventListener(Event.CLOSE, closeHandler);
socket.addEventListener(Event.CONNECT, connectHandler);
socket.addEventListener(IOErrorEvent.IO_ERROR,
ioErrorHandler);
}
private function closeHandler(event:Event):void
{
trace("Connection to [" + host + "] closed");
}
private function ioErrorHandler(event:IOErrorEvent):void
{
dispatchEvent(new Event(SOCK_IOERROR));
}
private function connectHandler(event:Event):void
{
trace("Connected to [" + host + "]");
dispatchEvent(new Event(SOCK_CONNECTED));
}
private function socketDataHandler(event:ProgressEvent):void
{
readMessage();
}
}
}
var sock:ChatSocket;
sock = new ChatSocket('127.0.0.1', 9990);
sock.addEventListener(ChatSocket.SOCK_CONNECTED, connected);
sock.addEventListener(ChatSocket.SOCK_IOERROR, ioError);
function ioError(e:Event):void
{
trace("Cant connect to " + sock.host + " on port " + sock.port);
}
function connected(e:Event):void
{
sock.sendMessage("are you hungry?");
}
ERROR IS:
localhost bin]$ ./mxmlc ChatSocket.as
Loading configuration file /home/lk/Documents/flex_sdk_3.4/frameworks/flex-config.xml
/home/lk/Documents/flex_sdk_3.4/bin/ChatSocket.as: Error: A file found in a source-path can not have more than one externally visible definition. ChatSocket;sock;ioError;connected
You may wish to check out gimite's web-socket-js. This is a socket gateway that conforms to the work-in-progress Web Socket API, so in future as browsers implement native WebSocket it will automatically switch over to the Flash-free alternative.
The following code lies outside the class and package {} blocks. That is not allowed.
var sock:ChatSocket;
sock = new ChatSocket('127.0.0.1', 9990);
sock.addEventListener(ChatSocket.SOCK_CONNECTED, connected);
sock.addEventListener(ChatSocket.SOCK_IOERROR, ioError);
function ioError(e:Event):void
{
trace("Cant connect to " + sock.host + " on port " + sock.port);
}
function connected(e:Event):void
{
sock.sendMessage("are you hungry?");
}
Declare a document class (that extends Sprite) and move ioError and connected methods to it. Make sock an instance variable instead of a local variable and add the declaration part of sock into its constructor.
//DocClass.as
package
{
public class DocClass
{
private var sock:ChatSocket;
public function DocClass()
{
sock = new ChatSocket('127.0.0.1', 9990);
sock.addEventListener(ChatSocket.SOCK_CONNECTED, connected);
sock.addEventListener(ChatSocket.SOCK_IOERROR, ioError);
}
private function ioError(e:Event):void
{
trace("Cant connect to " + sock.host + " on port " + sock.port);
}
private function connected(e:Event):void
{
sock.sendMessage("are you hungry?");
}
}
}

Categories